Salome HOME
Fix regressions
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140 //================================================================================
141 /*!
142  * \brief Initializes members by an existing element
143  *  \param [in] elem - the source element
144  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
145  */
146 //================================================================================
147
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
150 {
151   if ( elem )
152   {
153     myType = elem->GetType();
154     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
155     {
156       myIsPoly = elem->IsPoly();
157       if ( myIsPoly )
158       {
159         myIsQuad = elem->IsQuadratic();
160         if ( myType == SMDSAbs_Volume && !basicOnly )
161         {
162           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163           myPolyhedQuantities.swap( quant );
164         }
165       }
166     }
167     else if ( myType == SMDSAbs_Ball && !basicOnly )
168     {
169       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
170     }
171   }
172   return *this;
173 }
174
175 //=======================================================================
176 /*!
177  * \brief Add element
178  */
179 //=======================================================================
180
181 SMDS_MeshElement*
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183                              const ElemFeatures&                  features)
184 {
185   SMDS_MeshElement* e = 0;
186   int nbnode = node.size();
187   SMESHDS_Mesh* mesh = GetMeshDS();
188   const int ID = features.myID;
189
190   switch ( features.myType ) {
191   case SMDSAbs_Face:
192     if ( !features.myIsPoly ) {
193       if      (nbnode == 3) {
194         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195         else           e = mesh->AddFace      (node[0], node[1], node[2] );
196       }
197       else if (nbnode == 4) {
198         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
200       }
201       else if (nbnode == 6) {
202         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203                                                node[4], node[5], ID);
204         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
205                                                node[4], node[5] );
206       }
207       else if (nbnode == 7) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209                                                node[4], node[5], node[6], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6] );
212       }
213       else if (nbnode == 8) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], node[6], node[7], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7] );
218       }
219       else if (nbnode == 9) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], node[7], node[8], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8] );
224       }
225     }
226     else if ( !features.myIsQuad )
227     {
228       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229       else           e = mesh->AddPolygonalFace      (node    );
230     }
231     else if ( nbnode % 2 == 0 ) // just a protection
232     {
233       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234       else           e = mesh->AddQuadPolygonalFace      (node    );
235     }
236     break;
237
238   case SMDSAbs_Volume:
239     if ( !features.myIsPoly ) {
240       if      (nbnode == 4) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
243       }
244       else if (nbnode == 5) {
245         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
246                                                  node[4], ID);
247         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
248                                                  node[4] );
249       }
250       else if (nbnode == 6) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5] );
255       }
256       else if (nbnode == 8) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], node[5], node[6], node[7], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7] );
261       }
262       else if (nbnode == 10) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], ID);
266         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9] );
269       }
270       else if (nbnode == 12) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10], node[11], ID);
274         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11] );
277       }
278       else if (nbnode == 13) {
279         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],ID);
283         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10],node[11],
286                                                  node[12] );
287       }
288       else if (nbnode == 15) {
289         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290                                                  node[4], node[5], node[6], node[7],
291                                                  node[8], node[9], node[10],node[11],
292                                                  node[12],node[13],node[14],ID);
293         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
294                                                  node[4], node[5], node[6], node[7],
295                                                  node[8], node[9], node[10],node[11],
296                                                  node[12],node[13],node[14] );
297       }
298       else if (nbnode == 20) {
299         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300                                                  node[4], node[5], node[6], node[7],
301                                                  node[8], node[9], node[10],node[11],
302                                                  node[12],node[13],node[14],node[15],
303                                                  node[16],node[17],node[18],node[19],ID);
304         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
305                                                  node[4], node[5], node[6], node[7],
306                                                  node[8], node[9], node[10],node[11],
307                                                  node[12],node[13],node[14],node[15],
308                                                  node[16],node[17],node[18],node[19] );
309       }
310       else if (nbnode == 27) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],
316                                                  node[20],node[21],node[22],node[23],
317                                                  node[24],node[25],node[26], ID);
318         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
319                                                  node[4], node[5], node[6], node[7],
320                                                  node[8], node[9], node[10],node[11],
321                                                  node[12],node[13],node[14],node[15],
322                                                  node[16],node[17],node[18],node[19],
323                                                  node[20],node[21],node[22],node[23],
324                                                  node[24],node[25],node[26] );
325       }
326     }
327     else if ( !features.myIsQuad )
328     {
329       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
331     }
332     else
333     {
334       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
336     }
337     break;
338
339   case SMDSAbs_Edge:
340     if ( nbnode == 2 ) {
341       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342       else           e = mesh->AddEdge      (node[0], node[1] );
343     }
344     else if ( nbnode == 3 ) {
345       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
347     }
348     break;
349
350   case SMDSAbs_0DElement:
351     if ( nbnode == 1 ) {
352       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353       else           e = mesh->Add0DElement      (node[0] );
354     }
355     break;
356
357   case SMDSAbs_Node:
358     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
360     break;
361
362   case SMDSAbs_Ball:
363     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
365     break;
366
367   default:;
368   }
369   if ( e ) myLastCreatedElems.Append( e );
370   return e;
371 }
372
373 //=======================================================================
374 /*!
375  * \brief Add element
376  */
377 //=======================================================================
378
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380                                                const ElemFeatures& features)
381 {
382   vector<const SMDS_MeshNode*> nodes;
383   nodes.reserve( nodeIDs.size() );
384   vector<int>::const_iterator id = nodeIDs.begin();
385   while ( id != nodeIDs.end() ) {
386     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387       nodes.push_back( node );
388     else
389       return 0;
390   }
391   return AddElement( nodes, features );
392 }
393
394 //=======================================================================
395 //function : Remove
396 //purpose  : Remove a node or an element.
397 //           Modify a compute state of sub-meshes which become empty
398 //=======================================================================
399
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
401                               const bool         isNodes )
402 {
403   myLastCreatedElems.Clear();
404   myLastCreatedNodes.Clear();
405
406   SMESHDS_Mesh* aMesh = GetMeshDS();
407   set< SMESH_subMesh *> smmap;
408
409   int removed = 0;
410   list<int>::const_iterator it = theIDs.begin();
411   for ( ; it != theIDs.end(); it++ ) {
412     const SMDS_MeshElement * elem;
413     if ( isNodes )
414       elem = aMesh->FindNode( *it );
415     else
416       elem = aMesh->FindElement( *it );
417     if ( !elem )
418       continue;
419
420     // Notify VERTEX sub-meshes about modification
421     if ( isNodes ) {
422       const SMDS_MeshNode* node = cast2Node( elem );
423       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424         if ( int aShapeID = node->getshapeId() )
425           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
426             smmap.insert( sm );
427     }
428     // Find sub-meshes to notify about modification
429     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430     //     while ( nodeIt->more() ) {
431     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
433     //       if ( aPosition.get() ) {
434     //         if ( int aShapeID = aPosition->GetShapeId() ) {
435     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436     //             smmap.insert( sm );
437     //         }
438     //       }
439     //     }
440
441     // Do remove
442     if ( isNodes )
443       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
444     else
445       aMesh->RemoveElement( elem );
446     removed++;
447   }
448
449   // Notify sub-meshes about modification
450   if ( !smmap.empty() ) {
451     set< SMESH_subMesh *>::iterator smIt;
452     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
454   }
455
456   //   // Check if the whole mesh becomes empty
457   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
459
460   return removed;
461 }
462
463 //================================================================================
464 /*!
465  * \brief Create 0D elements on all nodes of the given object except those
466  *        nodes on which a 0D element already exists.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  */
471 //================================================================================
472
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474                                                    TIDSortedElemSet&       all0DElems )
475 {
476   SMDS_ElemIteratorPtr elemIt;
477   vector< const SMDS_MeshElement* > allNodes;
478   if ( elements.empty() )
479   {
480     allNodes.reserve( GetMeshDS()->NbNodes() );
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482     while ( elemIt->more() )
483       allNodes.push_back( elemIt->next() );
484
485     elemIt = elemSetIterator( allNodes );
486   }
487   else
488   {
489     elemIt = elemSetIterator( elements );
490   }
491
492   while ( elemIt->more() )
493   {
494     const SMDS_MeshElement* e = elemIt->next();
495     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496     while ( nodeIt->more() )
497     {
498       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
500       if ( it0D->more() )
501         all0DElems.insert( it0D->next() );
502       else {
503         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504         all0DElems.insert( myLastCreatedElems.Last() );
505       }
506     }
507   }
508 }
509
510 //=======================================================================
511 //function : FindShape
512 //purpose  : Return an index of the shape theElem is on
513 //           or zero if a shape not found
514 //=======================================================================
515
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 {
518   myLastCreatedElems.Clear();
519   myLastCreatedNodes.Clear();
520
521   SMESHDS_Mesh * aMesh = GetMeshDS();
522   if ( aMesh->ShapeToMesh().IsNull() )
523     return 0;
524
525   int aShapeID = theElem->getshapeId();
526   if ( aShapeID < 1 )
527     return 0;
528
529   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530     if ( sm->Contains( theElem ))
531       return aShapeID;
532
533   if ( theElem->GetType() == SMDSAbs_Node ) {
534     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
535   }
536   else {
537     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
538   }
539
540   TopoDS_Shape aShape; // the shape a node of theElem is on
541   if ( theElem->GetType() != SMDSAbs_Node )
542   {
543     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544     while ( nodeIt->more() ) {
545       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546       if ((aShapeID = node->getshapeId()) > 0) {
547         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548           if ( sm->Contains( theElem ))
549             return aShapeID;
550           if ( aShape.IsNull() )
551             aShape = aMesh->IndexToShape( aShapeID );
552         }
553       }
554     }
555   }
556
557   // None of nodes is on a proper shape,
558   // find the shape among ancestors of aShape on which a node is
559   if ( !aShape.IsNull() ) {
560     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561     for ( ; ancIt.More(); ancIt.Next() ) {
562       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563       if ( sm && sm->Contains( theElem ))
564         return aMesh->ShapeToIndex( ancIt.Value() );
565     }
566   }
567   else
568   {
569     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570     while ( const SMESHDS_SubMesh* sm = smIt->next() )
571       if ( sm->Contains( theElem ))
572         return sm->GetID();
573   }
574
575   return 0;
576 }
577
578 //=======================================================================
579 //function : IsMedium
580 //purpose  :
581 //=======================================================================
582
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
584                                 const SMDSAbs_ElementType typeToCheck)
585 {
586   bool isMedium = false;
587   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588   while (it->more() && !isMedium ) {
589     const SMDS_MeshElement* elem = it->next();
590     isMedium = elem->IsMediumNode(node);
591   }
592   return isMedium;
593 }
594
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose  : Shift nodes in the array corresponded to quadratic triangle
598 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
600
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
602 {
603   const SMDS_MeshNode* nd1 = aNodes[0];
604   aNodes[0] = aNodes[1];
605   aNodes[1] = aNodes[2];
606   aNodes[2] = nd1;
607   const SMDS_MeshNode* nd2 = aNodes[3];
608   aNodes[3] = aNodes[4];
609   aNodes[4] = aNodes[5];
610   aNodes[5] = nd2;
611 }
612
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose  : return number of the edges connected with the theNode.
616 //           if theEdges has connections with the other type of the
617 //           elements, return -1
618 //=======================================================================
619
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
621 {
622   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
623   // int nb=0;
624   // while(elemIt->more()) {
625   //   elemIt->next();
626   //   nb++;
627   // }
628   // return nb;
629   return theNode->NbInverseElements();
630 }
631
632 //=======================================================================
633 //function : getNodesFromTwoTria
634 //purpose  : 
635 //=======================================================================
636
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638                                 const SMDS_MeshElement * theTria2,
639                                 vector< const SMDS_MeshNode*>& N1,
640                                 vector< const SMDS_MeshNode*>& N2)
641 {
642   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643   if ( N1.size() < 6 ) return false;
644   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645   if ( N2.size() < 6 ) return false;
646
647   int sames[3] = {-1,-1,-1};
648   int nbsames = 0;
649   int i, j;
650   for(i=0; i<3; i++) {
651     for(j=0; j<3; j++) {
652       if(N1[i]==N2[j]) {
653         sames[i] = j;
654         nbsames++;
655         break;
656       }
657     }
658   }
659   if(nbsames!=2) return false;
660   if(sames[0]>-1) {
661     shiftNodesQuadTria(N1);
662     if(sames[1]>-1) {
663       shiftNodesQuadTria(N1);
664     }
665   }
666   i = sames[0] + sames[1] + sames[2];
667   for(; i<2; i++) {
668     shiftNodesQuadTria(N2);
669   }
670   // now we receive following N1 and N2 (using numeration as in the image below)
671   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
672   // i.e. first nodes from both arrays form a new diagonal
673   return true;
674 }
675
676 //=======================================================================
677 //function : InverseDiag
678 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
679 //           but having other common link.
680 //           Return False if args are improper
681 //=======================================================================
682
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684                                     const SMDS_MeshElement * theTria2 )
685 {
686   myLastCreatedElems.Clear();
687   myLastCreatedNodes.Clear();
688
689   if (!theTria1 || !theTria2)
690     return false;
691
692   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
693   if (!F1) return false;
694   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
695   if (!F2) return false;
696   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
697       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
698
699     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
700     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
701     //    |/ |                                         | \|
702     //  B +--+ 2                                     B +--+ 2
703
704     // put nodes in array and find out indices of the same ones
705     const SMDS_MeshNode* aNodes [6];
706     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
707     int i = 0;
708     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
709     while ( it->more() ) {
710       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
711
712       if ( i > 2 ) // theTria2
713         // find same node of theTria1
714         for ( int j = 0; j < 3; j++ )
715           if ( aNodes[ i ] == aNodes[ j ]) {
716             sameInd[ j ] = i;
717             sameInd[ i ] = j;
718             break;
719           }
720       // next
721       i++;
722       if ( i == 3 ) {
723         if ( it->more() )
724           return false; // theTria1 is not a triangle
725         it = theTria2->nodesIterator();
726       }
727       if ( i == 6 && it->more() )
728         return false; // theTria2 is not a triangle
729     }
730
731     // find indices of 1,2 and of A,B in theTria1
732     int iA = -1, iB = 0, i1 = 0, i2 = 0;
733     for ( i = 0; i < 6; i++ ) {
734       if ( sameInd [ i ] == -1 ) {
735         if ( i < 3 ) i1 = i;
736         else         i2 = i;
737       }
738       else if (i < 3) {
739         if ( iA >= 0) iB = i;
740         else          iA = i;
741       }
742     }
743     // nodes 1 and 2 should not be the same
744     if ( aNodes[ i1 ] == aNodes[ i2 ] )
745       return false;
746
747     // theTria1: A->2
748     aNodes[ iA ] = aNodes[ i2 ];
749     // theTria2: B->1
750     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
751
752     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
753     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
754
755     return true;
756
757   } // end if(F1 && F2)
758
759   // check case of quadratic faces
760   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
761       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
762     return false;
763   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
764       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
765     return false;
766
767   //       5
768   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
769   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
770   //    |   / |
771   //  7 +  +  + 6
772   //    | /9  |
773   //    |/    |
774   //  4 +--+--+ 3
775   //       8
776
777   vector< const SMDS_MeshNode* > N1;
778   vector< const SMDS_MeshNode* > N2;
779   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
780     return false;
781   // now we receive following N1 and N2 (using numeration as above image)
782   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
783   // i.e. first nodes from both arrays determ new diagonal
784
785   vector< const SMDS_MeshNode*> N1new( N1.size() );
786   vector< const SMDS_MeshNode*> N2new( N2.size() );
787   N1new.back() = N1.back(); // central node of biquadratic
788   N2new.back() = N2.back();
789   N1new[0] = N1[0];  N2new[0] = N1[0];
790   N1new[1] = N2[0];  N2new[1] = N1[1];
791   N1new[2] = N2[1];  N2new[2] = N2[0];
792   N1new[3] = N1[4];  N2new[3] = N1[3];
793   N1new[4] = N2[3];  N2new[4] = N2[5];
794   N1new[5] = N1[5];  N2new[5] = N1[4];
795   // change nodes in faces
796   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
797   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
798
799   // move the central node of biquadratic triangle
800   SMESH_MesherHelper helper( *GetMesh() );
801   for ( int is2nd = 0; is2nd < 2; ++is2nd )
802   {
803     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
804     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
805     if ( nodes.size() < 7 )
806       continue;
807     helper.SetSubShape( tria->getshapeId() );
808     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
809     gp_Pnt xyz;
810     if ( F.IsNull() )
811     {
812       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
813               SMESH_TNodeXYZ( nodes[4] ) +
814               SMESH_TNodeXYZ( nodes[5] )) / 3.;
815     }
816     else
817     {
818       bool checkUV;
819       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
820                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
821                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
822       TopLoc_Location loc;
823       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
824       xyz = S->Value( uv.X(), uv.Y() );
825       xyz.Transform( loc );
826       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
827            nodes[6]->getshapeId() > 0 )
828         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
829     }
830     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
831   }
832   return true;
833 }
834
835 //=======================================================================
836 //function : findTriangles
837 //purpose  : find triangles sharing theNode1-theNode2 link
838 //=======================================================================
839
840 static bool findTriangles(const SMDS_MeshNode *    theNode1,
841                           const SMDS_MeshNode *    theNode2,
842                           const SMDS_MeshElement*& theTria1,
843                           const SMDS_MeshElement*& theTria2)
844 {
845   if ( !theNode1 || !theNode2 ) return false;
846
847   theTria1 = theTria2 = 0;
848
849   set< const SMDS_MeshElement* > emap;
850   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
851   while (it->more()) {
852     const SMDS_MeshElement* elem = it->next();
853     if ( elem->NbCornerNodes() == 3 )
854       emap.insert( elem );
855   }
856   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
857   while (it->more()) {
858     const SMDS_MeshElement* elem = it->next();
859     if ( emap.count( elem )) {
860       if ( !theTria1 )
861       {
862         theTria1 = elem;
863       }
864       else  
865       {
866         theTria2 = elem;
867         // theTria1 must be element with minimum ID
868         if ( theTria2->GetID() < theTria1->GetID() )
869           std::swap( theTria2, theTria1 );
870         return true;
871       }
872     }
873   }
874   return false;
875 }
876
877 //=======================================================================
878 //function : InverseDiag
879 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
880 //           with ones built on the same 4 nodes but having other common link.
881 //           Return false if proper faces not found
882 //=======================================================================
883
884 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
885                                     const SMDS_MeshNode * theNode2)
886 {
887   myLastCreatedElems.Clear();
888   myLastCreatedNodes.Clear();
889
890   const SMDS_MeshElement *tr1, *tr2;
891   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
892     return false;
893
894   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
895   if (!F1) return false;
896   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
897   if (!F2) return false;
898   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
899       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
900
901     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
902     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
903     //    |/ |                                    | \|
904     //  B +--+ 2                                B +--+ 2
905
906     // put nodes in array
907     // and find indices of 1,2 and of A in tr1 and of B in tr2
908     int i, iA1 = 0, i1 = 0;
909     const SMDS_MeshNode* aNodes1 [3];
910     SMDS_ElemIteratorPtr it;
911     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
912       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
913       if ( aNodes1[ i ] == theNode1 )
914         iA1 = i; // node A in tr1
915       else if ( aNodes1[ i ] != theNode2 )
916         i1 = i;  // node 1
917     }
918     int iB2 = 0, i2 = 0;
919     const SMDS_MeshNode* aNodes2 [3];
920     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
921       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
922       if ( aNodes2[ i ] == theNode2 )
923         iB2 = i; // node B in tr2
924       else if ( aNodes2[ i ] != theNode1 )
925         i2 = i;  // node 2
926     }
927
928     // nodes 1 and 2 should not be the same
929     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
930       return false;
931
932     // tr1: A->2
933     aNodes1[ iA1 ] = aNodes2[ i2 ];
934     // tr2: B->1
935     aNodes2[ iB2 ] = aNodes1[ i1 ];
936
937     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
938     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
939
940     return true;
941   }
942
943   // check case of quadratic faces
944   return InverseDiag(tr1,tr2);
945 }
946
947 //=======================================================================
948 //function : getQuadrangleNodes
949 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
950 //           fusion of triangles tr1 and tr2 having shared link on
951 //           theNode1 and theNode2
952 //=======================================================================
953
954 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
955                         const SMDS_MeshNode *    theNode1,
956                         const SMDS_MeshNode *    theNode2,
957                         const SMDS_MeshElement * tr1,
958                         const SMDS_MeshElement * tr2 )
959 {
960   if( tr1->NbNodes() != tr2->NbNodes() )
961     return false;
962   // find the 4-th node to insert into tr1
963   const SMDS_MeshNode* n4 = 0;
964   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
965   int i=0;
966   while ( !n4 && i<3 ) {
967     const SMDS_MeshNode * n = cast2Node( it->next() );
968     i++;
969     bool isDiag = ( n == theNode1 || n == theNode2 );
970     if ( !isDiag )
971       n4 = n;
972   }
973   // Make an array of nodes to be in a quadrangle
974   int iNode = 0, iFirstDiag = -1;
975   it = tr1->nodesIterator();
976   i=0;
977   while ( i<3 ) {
978     const SMDS_MeshNode * n = cast2Node( it->next() );
979     i++;
980     bool isDiag = ( n == theNode1 || n == theNode2 );
981     if ( isDiag ) {
982       if ( iFirstDiag < 0 )
983         iFirstDiag = iNode;
984       else if ( iNode - iFirstDiag == 1 )
985         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
986     }
987     else if ( n == n4 ) {
988       return false; // tr1 and tr2 should not have all the same nodes
989     }
990     theQuadNodes[ iNode++ ] = n;
991   }
992   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
993     theQuadNodes[ iNode ] = n4;
994
995   return true;
996 }
997
998 //=======================================================================
999 //function : DeleteDiag
1000 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1001 //           with a quadrangle built on the same 4 nodes.
1002 //           Return false if proper faces not found
1003 //=======================================================================
1004
1005 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1006                                    const SMDS_MeshNode * theNode2)
1007 {
1008   myLastCreatedElems.Clear();
1009   myLastCreatedNodes.Clear();
1010
1011   const SMDS_MeshElement *tr1, *tr2;
1012   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1013     return false;
1014
1015   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1016   if (!F1) return false;
1017   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1018   if (!F2) return false;
1019   SMESHDS_Mesh * aMesh = GetMeshDS();
1020
1021   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1022       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1023
1024     const SMDS_MeshNode* aNodes [ 4 ];
1025     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1026       return false;
1027
1028     const SMDS_MeshElement* newElem = 0;
1029     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1030     myLastCreatedElems.Append(newElem);
1031     AddToSameGroups( newElem, tr1, aMesh );
1032     int aShapeId = tr1->getshapeId();
1033     if ( aShapeId )
1034       {
1035         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1036       }
1037     aMesh->RemoveElement( tr1 );
1038     aMesh->RemoveElement( tr2 );
1039
1040     return true;
1041   }
1042
1043   // check case of quadratic faces
1044   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1045     return false;
1046   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1047     return false;
1048
1049   //       5
1050   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1051   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1052   //    |   / |
1053   //  7 +  +  + 6
1054   //    | /9  |
1055   //    |/    |
1056   //  4 +--+--+ 3
1057   //       8
1058
1059   vector< const SMDS_MeshNode* > N1;
1060   vector< const SMDS_MeshNode* > N2;
1061   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1062     return false;
1063   // now we receive following N1 and N2 (using numeration as above image)
1064   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1065   // i.e. first nodes from both arrays determ new diagonal
1066
1067   const SMDS_MeshNode* aNodes[8];
1068   aNodes[0] = N1[0];
1069   aNodes[1] = N1[1];
1070   aNodes[2] = N2[0];
1071   aNodes[3] = N2[1];
1072   aNodes[4] = N1[3];
1073   aNodes[5] = N2[5];
1074   aNodes[6] = N2[3];
1075   aNodes[7] = N1[5];
1076
1077   const SMDS_MeshElement* newElem = 0;
1078   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1079                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1080   myLastCreatedElems.Append(newElem);
1081   AddToSameGroups( newElem, tr1, aMesh );
1082   int aShapeId = tr1->getshapeId();
1083   if ( aShapeId )
1084     {
1085       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1086     }
1087   aMesh->RemoveElement( tr1 );
1088   aMesh->RemoveElement( tr2 );
1089
1090   // remove middle node (9)
1091   GetMeshDS()->RemoveNode( N1[4] );
1092
1093   return true;
1094 }
1095
1096 //=======================================================================
1097 //function : Reorient
1098 //purpose  : Reverse theElement orientation
1099 //=======================================================================
1100
1101 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1102 {
1103   myLastCreatedElems.Clear();
1104   myLastCreatedNodes.Clear();
1105
1106   if (!theElem)
1107     return false;
1108   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1109   if ( !it || !it->more() )
1110     return false;
1111
1112   const SMDSAbs_ElementType type = theElem->GetType();
1113   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1114     return false;
1115
1116   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1117   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1118   {
1119     const SMDS_VtkVolume* aPolyedre =
1120       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1121     if (!aPolyedre) {
1122       MESSAGE("Warning: bad volumic element");
1123       return false;
1124     }
1125     const int nbFaces = aPolyedre->NbFaces();
1126     vector<const SMDS_MeshNode *> poly_nodes;
1127     vector<int> quantities (nbFaces);
1128
1129     // reverse each face of the polyedre
1130     for (int iface = 1; iface <= nbFaces; iface++) {
1131       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1132       quantities[iface - 1] = nbFaceNodes;
1133
1134       for (inode = nbFaceNodes; inode >= 1; inode--) {
1135         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1136         poly_nodes.push_back(curNode);
1137       }
1138     }
1139     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1140   }
1141   else // other elements
1142   {
1143     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145     if ( interlace.empty() )
1146     {
1147       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1148     }
1149     else
1150     {
1151       SMDS_MeshCell::applyInterlace( interlace, nodes );
1152     }
1153     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1154   }
1155   return false;
1156 }
1157
1158 //================================================================================
1159 /*!
1160  * \brief Reorient faces.
1161  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162  * \param theDirection - desired direction of normal of \a theFace
1163  * \param theFace - one of \a theFaces that sould be oriented according to
1164  *        \a theDirection and whose orientation defines orientation of other faces
1165  * \return number of reoriented faces.
1166  */
1167 //================================================================================
1168
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1170                                   const gp_Dir&            theDirection,
1171                                   const SMDS_MeshElement * theFace)
1172 {
1173   int nbReori = 0;
1174   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1175
1176   if ( theFaces.empty() )
1177   {
1178     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1179     while ( fIt->more() )
1180       theFaces.insert( theFaces.end(), fIt->next() );
1181   }
1182
1183   // orient theFace according to theDirection
1184   gp_XYZ normal;
1185   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186   if ( normal * theDirection.XYZ() < 0 )
1187     nbReori += Reorient( theFace );
1188
1189   // Orient other faces
1190
1191   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192   TIDSortedElemSet avoidSet;
1193   set< SMESH_TLink > checkedLinks;
1194   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1195
1196   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197     theFaces.erase( theFace );
1198   startFaces.insert( theFace );
1199
1200   int nodeInd1, nodeInd2;
1201   const SMDS_MeshElement*           otherFace;
1202   vector< const SMDS_MeshElement* > facesNearLink;
1203   vector< std::pair< int, int > >   nodeIndsOfFace;
1204
1205   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206   while ( !startFaces.empty() )
1207   {
1208     startFace = startFaces.begin();
1209     theFace = *startFace;
1210     startFaces.erase( startFace );
1211     if ( !visitedFaces.insert( theFace ).second )
1212       continue;
1213
1214     avoidSet.clear();
1215     avoidSet.insert(theFace);
1216
1217     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1218
1219     const int nbNodes = theFace->NbCornerNodes();
1220     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1221     {
1222       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223       linkIt_isNew = checkedLinks.insert( link );
1224       if ( !linkIt_isNew.second )
1225       {
1226         // link has already been checked and won't be encountered more
1227         // if the group (theFaces) is manifold
1228         //checkedLinks.erase( linkIt_isNew.first );
1229       }
1230       else
1231       {
1232         facesNearLink.clear();
1233         nodeIndsOfFace.clear();
1234         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1235                                                              theFaces, avoidSet,
1236                                                              &nodeInd1, &nodeInd2 )))
1237           if ( otherFace != theFace)
1238           {
1239             facesNearLink.push_back( otherFace );
1240             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241             avoidSet.insert( otherFace );
1242           }
1243         if ( facesNearLink.size() > 1 )
1244         {
1245           // NON-MANIFOLD mesh shell !
1246           // select a face most co-directed with theFace,
1247           // other faces won't be visited this time
1248           gp_XYZ NF, NOF;
1249           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250           double proj, maxProj = -1;
1251           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253             if (( proj = Abs( NF * NOF )) > maxProj ) {
1254               maxProj = proj;
1255               otherFace = facesNearLink[i];
1256               nodeInd1  = nodeIndsOfFace[i].first;
1257               nodeInd2  = nodeIndsOfFace[i].second;
1258             }
1259           }
1260           // not to visit rejected faces
1261           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263               visitedFaces.insert( facesNearLink[i] );
1264         }
1265         else if ( facesNearLink.size() == 1 )
1266         {
1267           otherFace = facesNearLink[0];
1268           nodeInd1  = nodeIndsOfFace.back().first;
1269           nodeInd2  = nodeIndsOfFace.back().second;
1270         }
1271         if ( otherFace && otherFace != theFace)
1272         {
1273           // link must be reverse in otherFace if orientation ot otherFace
1274           // is same as that of theFace
1275           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1276           {
1277             nbReori += Reorient( otherFace );
1278           }
1279           startFaces.insert( otherFace );
1280         }
1281       }
1282       std::swap( link.first, link.second ); // reverse the link
1283     }
1284   }
1285   return nbReori;
1286 }
1287
1288 //================================================================================
1289 /*!
1290  * \brief Reorient faces basing on orientation of adjacent volumes.
1291  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292  * \param theVolumes - reference volumes.
1293  * \param theOutsideNormal - to orient faces to have their normal
1294  *        pointing either \a outside or \a inside the adjacent volumes.
1295  * \return number of reoriented faces.
1296  */
1297 //================================================================================
1298
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300                                       TIDSortedElemSet & theVolumes,
1301                                       const bool         theOutsideNormal)
1302 {
1303   int nbReori = 0;
1304
1305   SMDS_ElemIteratorPtr faceIt;
1306   if ( theFaces.empty() )
1307     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1308   else
1309     faceIt = elemSetIterator( theFaces );
1310
1311   vector< const SMDS_MeshNode* > faceNodes;
1312   TIDSortedElemSet checkedVolumes;
1313   set< const SMDS_MeshNode* > faceNodesSet;
1314   SMDS_VolumeTool volumeTool;
1315
1316   while ( faceIt->more() ) // loop on given faces
1317   {
1318     const SMDS_MeshElement* face = faceIt->next();
1319     if ( face->GetType() != SMDSAbs_Face )
1320       continue;
1321
1322     const size_t nbCornersNodes = face->NbCornerNodes();
1323     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1324
1325     checkedVolumes.clear();
1326     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327     while ( vIt->more() )
1328     {
1329       const SMDS_MeshElement* volume = vIt->next();
1330
1331       if ( !checkedVolumes.insert( volume ).second )
1332         continue;
1333       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1334         continue;
1335
1336       // is volume adjacent?
1337       bool allNodesCommon = true;
1338       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340       if ( !allNodesCommon )
1341         continue;
1342
1343       // get nodes of a corresponding volume facet
1344       faceNodesSet.clear();
1345       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346       volumeTool.Set( volume );
1347       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348       if ( facetID < 0 ) continue;
1349       volumeTool.SetExternalNormal();
1350       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1351
1352       // compare order of faceNodes and facetNodes
1353       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1354       int iNN[2];
1355       for ( int i = 0; i < 2; ++i )
1356       {
1357         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359           if ( faceNodes[ iN ] == n )
1360           {
1361             iNN[ i ] = iN;
1362             break;
1363           }
1364       }
1365       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366       if ( isOutside != theOutsideNormal )
1367         nbReori += Reorient( face );
1368     }
1369   }  // loop on given faces
1370
1371   return nbReori;
1372 }
1373
1374 //=======================================================================
1375 //function : getBadRate
1376 //purpose  :
1377 //=======================================================================
1378
1379 static double getBadRate (const SMDS_MeshElement*               theElem,
1380                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1381 {
1382   SMESH::Controls::TSequenceOfXYZ P;
1383   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1384     return 1e100;
1385   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1387 }
1388
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose  : Cut quadrangles into triangles.
1392 //           theCrit is used to select a diagonal to cut
1393 //=======================================================================
1394
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1396                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1397 {
1398   myLastCreatedElems.Clear();
1399   myLastCreatedNodes.Clear();
1400
1401   if ( !theCrit.get() )
1402     return false;
1403
1404   SMESHDS_Mesh * aMesh = GetMeshDS();
1405
1406   Handle(Geom_Surface) surface;
1407   SMESH_MesherHelper   helper( *GetMesh() );
1408
1409   TIDSortedElemSet::iterator itElem;
1410   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1411   {
1412     const SMDS_MeshElement* elem = *itElem;
1413     if ( !elem || elem->GetType() != SMDSAbs_Face )
1414       continue;
1415     if ( elem->NbCornerNodes() != 4 )
1416       continue;
1417
1418     // retrieve element nodes
1419     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1420
1421     // compare two sets of possible triangles
1422     double aBadRate1, aBadRate2; // to what extent a set is bad
1423     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1426
1427     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1430
1431     const int aShapeId = FindShape( elem );
1432     const SMDS_MeshElement* newElem1 = 0;
1433     const SMDS_MeshElement* newElem2 = 0;
1434
1435     if ( !elem->IsQuadratic() ) // split liner quadrangle
1436     {
1437       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439       if ( aBadRate1 <= aBadRate2 ) {
1440         // tr1 + tr2 is better
1441         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1443       }
1444       else {
1445         // tr3 + tr4 is better
1446         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1448       }
1449     }
1450     else // split quadratic quadrangle
1451     {
1452       helper.SetIsQuadratic( true );
1453       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1454
1455       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456       if ( aNodes.size() == 9 )
1457       {
1458         helper.SetIsBiQuadratic( true );
1459         if ( aBadRate1 <= aBadRate2 )
1460           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1461         else
1462           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1463       }
1464       // create a new element
1465       if ( aBadRate1 <= aBadRate2 ) {
1466         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1468       }
1469       else {
1470         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1472       }
1473     } // quadratic case
1474
1475     // care of a new element
1476
1477     myLastCreatedElems.Append(newElem1);
1478     myLastCreatedElems.Append(newElem2);
1479     AddToSameGroups( newElem1, elem, aMesh );
1480     AddToSameGroups( newElem2, elem, aMesh );
1481
1482     // put a new triangle on the same shape
1483     if ( aShapeId )
1484       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1486
1487     aMesh->RemoveElement( elem );
1488   }
1489   return true;
1490 }
1491
1492 //=======================================================================
1493 /*!
1494  * \brief Split each of given quadrangles into 4 triangles.
1495  * \param theElems - The faces to be splitted. If empty all faces are split.
1496  */
1497 //=======================================================================
1498
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1500 {
1501   myLastCreatedElems.Clear();
1502   myLastCreatedNodes.Clear();
1503
1504   SMESH_MesherHelper helper( *GetMesh() );
1505   helper.SetElementsOnShape( true );
1506
1507   SMDS_ElemIteratorPtr faceIt;
1508   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1509   else                    faceIt = elemSetIterator( theElems );
1510
1511   bool   checkUV;
1512   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1513   gp_XYZ xyz[9];
1514   vector< const SMDS_MeshNode* > nodes;
1515   SMESHDS_SubMesh*               subMeshDS = 0;
1516   TopoDS_Face                    F;
1517   Handle(Geom_Surface)           surface;
1518   TopLoc_Location                loc;
1519
1520   while ( faceIt->more() )
1521   {
1522     const SMDS_MeshElement* quad = faceIt->next();
1523     if ( !quad || quad->NbCornerNodes() != 4 )
1524       continue;
1525
1526     // get a surface the quad is on
1527
1528     if ( quad->getshapeId() < 1 )
1529     {
1530       F.Nullify();
1531       helper.SetSubShape( 0 );
1532       subMeshDS = 0;
1533     }
1534     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1535     {
1536       helper.SetSubShape( quad->getshapeId() );
1537       if ( !helper.GetSubShape().IsNull() &&
1538            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1539       {
1540         F = TopoDS::Face( helper.GetSubShape() );
1541         surface = BRep_Tool::Surface( F, loc );
1542         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1543       }
1544       else
1545       {
1546         helper.SetSubShape( 0 );
1547         subMeshDS = 0;
1548       }
1549     }
1550
1551     // create a central node
1552
1553     const SMDS_MeshNode* nCentral;
1554     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1555
1556     if ( nodes.size() == 9 )
1557     {
1558       nCentral = nodes.back();
1559     }
1560     else
1561     {
1562       size_t iN = 0;
1563       if ( F.IsNull() )
1564       {
1565         for ( ; iN < nodes.size(); ++iN )
1566           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1567
1568         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1569           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1570
1571         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572                                    xyz[0], xyz[1], xyz[2], xyz[3],
1573                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1574       }
1575       else
1576       {
1577         for ( ; iN < nodes.size(); ++iN )
1578           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1579
1580         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1581           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1582
1583         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584                                   uv[0], uv[1], uv[2], uv[3],
1585                                   uv[4], uv[5], uv[6], uv[7] );
1586
1587         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1588         xyz[ 8 ] = p.XYZ();
1589       }
1590
1591       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1592                                  uv[8].X(), uv[8].Y() );
1593       myLastCreatedNodes.Append( nCentral );
1594     }
1595
1596     // create 4 triangles
1597
1598     helper.SetIsQuadratic  ( nodes.size() > 4 );
1599     helper.SetIsBiQuadratic( nodes.size() == 9 );
1600     if ( helper.GetIsQuadratic() )
1601       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1602
1603     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1604
1605     for ( int i = 0; i < 4; ++i )
1606     {
1607       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1608                                                nodes[(i+1)%4],
1609                                                nCentral );
1610       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1611       myLastCreatedElems.Append( tria );
1612     }
1613   }
1614 }
1615
1616 //=======================================================================
1617 //function : BestSplit
1618 //purpose  : Find better diagonal for cutting.
1619 //=======================================================================
1620
1621 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1622                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1623 {
1624   myLastCreatedElems.Clear();
1625   myLastCreatedNodes.Clear();
1626
1627   if (!theCrit.get())
1628     return -1;
1629
1630   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1631     return -1;
1632
1633   if( theQuad->NbNodes()==4 ||
1634       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1635
1636     // retrieve element nodes
1637     const SMDS_MeshNode* aNodes [4];
1638     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1639     int i = 0;
1640     //while (itN->more())
1641     while (i<4) {
1642       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1643     }
1644     // compare two sets of possible triangles
1645     double aBadRate1, aBadRate2; // to what extent a set is bad
1646     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1647     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1648     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1649
1650     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1651     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1652     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1653     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1654     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1655     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1656       return 1; // diagonal 1-3
1657
1658     return 2; // diagonal 2-4
1659   }
1660   return -1;
1661 }
1662
1663 namespace
1664 {
1665   // Methods of splitting volumes into tetra
1666
1667   const int theHexTo5_1[5*4+1] =
1668     {
1669       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1670     };
1671   const int theHexTo5_2[5*4+1] =
1672     {
1673       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1674     };
1675   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1676
1677   const int theHexTo6_1[6*4+1] =
1678     {
1679       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1680     };
1681   const int theHexTo6_2[6*4+1] =
1682     {
1683       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1684     };
1685   const int theHexTo6_3[6*4+1] =
1686     {
1687       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1688     };
1689   const int theHexTo6_4[6*4+1] =
1690     {
1691       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1692     };
1693   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1694
1695   const int thePyraTo2_1[2*4+1] =
1696     {
1697       0, 1, 2, 4,    0, 2, 3, 4,   -1
1698     };
1699   const int thePyraTo2_2[2*4+1] =
1700     {
1701       1, 2, 3, 4,    1, 3, 0, 4,   -1
1702     };
1703   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1704
1705   const int thePentaTo3_1[3*4+1] =
1706     {
1707       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1708     };
1709   const int thePentaTo3_2[3*4+1] =
1710     {
1711       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1712     };
1713   const int thePentaTo3_3[3*4+1] =
1714     {
1715       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1716     };
1717   const int thePentaTo3_4[3*4+1] =
1718     {
1719       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1720     };
1721   const int thePentaTo3_5[3*4+1] =
1722     {
1723       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1724     };
1725   const int thePentaTo3_6[3*4+1] =
1726     {
1727       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1728     };
1729   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1730                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1731
1732   // Methods of splitting hexahedron into prisms
1733
1734   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1735     {
1736       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1737     };
1738   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1739     {
1740       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1741     };
1742   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1743     {
1744       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1745     };
1746
1747   const int theHexTo2Prisms_BT_1[6*2+1] =
1748     {
1749       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1750     };
1751   const int theHexTo2Prisms_BT_2[6*2+1] =
1752     {
1753       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1754     };
1755   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1756
1757   const int theHexTo2Prisms_LR_1[6*2+1] =
1758     {
1759       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1760     };
1761   const int theHexTo2Prisms_LR_2[6*2+1] =
1762     {
1763       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1764     };
1765   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1766
1767   const int theHexTo2Prisms_FB_1[6*2+1] =
1768     {
1769       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1770     };
1771   const int theHexTo2Prisms_FB_2[6*2+1] =
1772     {
1773       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1774     };
1775   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1776
1777
1778   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1779   {
1780     int _n1, _n2, _n3;
1781     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1782     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1783     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1784                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1785   };
1786   struct TSplitMethod
1787   {
1788     int        _nbSplits;
1789     int        _nbCorners;
1790     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1791     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1792     bool       _ownConn;      //!< to delete _connectivity in destructor
1793     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1794
1795     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1796       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1797     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1798     bool hasFacet( const TTriangleFacet& facet ) const
1799     {
1800       if ( _nbCorners == 4 )
1801       {
1802         const int* tetConn = _connectivity;
1803         for ( ; tetConn[0] >= 0; tetConn += 4 )
1804           if (( facet.contains( tetConn[0] ) +
1805                 facet.contains( tetConn[1] ) +
1806                 facet.contains( tetConn[2] ) +
1807                 facet.contains( tetConn[3] )) == 3 )
1808             return true;
1809       }
1810       else // prism, _nbCorners == 6
1811       {
1812         const int* prismConn = _connectivity;
1813         for ( ; prismConn[0] >= 0; prismConn += 6 )
1814         {
1815           if (( facet.contains( prismConn[0] ) &&
1816                 facet.contains( prismConn[1] ) &&
1817                 facet.contains( prismConn[2] ))
1818               ||
1819               ( facet.contains( prismConn[3] ) &&
1820                 facet.contains( prismConn[4] ) &&
1821                 facet.contains( prismConn[5] )))
1822             return true;
1823         }
1824       }
1825       return false;
1826     }
1827   };
1828
1829   //=======================================================================
1830   /*!
1831    * \brief return TSplitMethod for the given element to split into tetrahedra
1832    */
1833   //=======================================================================
1834
1835   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1836   {
1837     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1838
1839     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1840     // an edge and a face barycenter; tertaherdons are based on triangles and
1841     // a volume barycenter
1842     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1843
1844     // Find out how adjacent volumes are split
1845
1846     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1847     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1848     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1849     {
1850       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1851       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1852       if ( nbNodes < 4 ) continue;
1853
1854       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1855       const int* nInd = vol.GetFaceNodesIndices( iF );
1856       if ( nbNodes == 4 )
1857       {
1858         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1859         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1860         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1861         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1862       }
1863       else
1864       {
1865         int iCom = 0; // common node of triangle faces to split into
1866         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1867         {
1868           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1869                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1870                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1871           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1872                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1873                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1874           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1875           {
1876             triaSplits.push_back( t012 );
1877             triaSplits.push_back( t023 );
1878             break;
1879           }
1880         }
1881       }
1882       if ( !triaSplits.empty() )
1883         hasAdjacentSplits = true;
1884     }
1885
1886     // Among variants of split method select one compliant with adjacent volumes
1887
1888     TSplitMethod method;
1889     if ( !vol.Element()->IsPoly() && !is24TetMode )
1890     {
1891       int nbVariants = 2, nbTet = 0;
1892       const int** connVariants = 0;
1893       switch ( vol.Element()->GetEntityType() )
1894       {
1895       case SMDSEntity_Hexa:
1896       case SMDSEntity_Quad_Hexa:
1897       case SMDSEntity_TriQuad_Hexa:
1898         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1899           connVariants = theHexTo5, nbTet = 5;
1900         else
1901           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1902         break;
1903       case SMDSEntity_Pyramid:
1904       case SMDSEntity_Quad_Pyramid:
1905         connVariants = thePyraTo2;  nbTet = 2;
1906         break;
1907       case SMDSEntity_Penta:
1908       case SMDSEntity_Quad_Penta:
1909         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1910         break;
1911       default:
1912         nbVariants = 0;
1913       }
1914       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1915       {
1916         // check method compliancy with adjacent tetras,
1917         // all found splits must be among facets of tetras described by this method
1918         method = TSplitMethod( nbTet, connVariants[variant] );
1919         if ( hasAdjacentSplits && method._nbSplits > 0 )
1920         {
1921           bool facetCreated = true;
1922           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1923           {
1924             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1925             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1926               facetCreated = method.hasFacet( *facet );
1927           }
1928           if ( !facetCreated )
1929             method = TSplitMethod(0); // incompatible method
1930         }
1931       }
1932     }
1933     if ( method._nbSplits < 1 )
1934     {
1935       // No standard method is applicable, use a generic solution:
1936       // each facet of a volume is split into triangles and
1937       // each of triangles and a volume barycenter form a tetrahedron.
1938
1939       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1940
1941       int* connectivity = new int[ maxTetConnSize + 1 ];
1942       method._connectivity = connectivity;
1943       method._ownConn = true;
1944       method._baryNode = !isHex27; // to create central node or not
1945
1946       int connSize = 0;
1947       int baryCenInd = vol.NbNodes() - int( isHex27 );
1948       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1949       {
1950         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1951         const int*   nInd = vol.GetFaceNodesIndices( iF );
1952         // find common node of triangle facets of tetra to create
1953         int iCommon = 0; // index in linear numeration
1954         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1955         if ( !triaSplits.empty() )
1956         {
1957           // by found facets
1958           const TTriangleFacet* facet = &triaSplits.front();
1959           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1960             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1961                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1962               break;
1963         }
1964         else if ( nbNodes > 3 && !is24TetMode )
1965         {
1966           // find the best method of splitting into triangles by aspect ratio
1967           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1968           map< double, int > badness2iCommon;
1969           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1970           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1971           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1972           {
1973             double badness = 0;
1974             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1975             {
1976               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1977                                       nodes[ iQ*((iLast-1)%nbNodes)],
1978                                       nodes[ iQ*((iLast  )%nbNodes)]);
1979               badness += getBadRate( &tria, aspectRatio );
1980             }
1981             badness2iCommon.insert( make_pair( badness, iCommon ));
1982           }
1983           // use iCommon with lowest badness
1984           iCommon = badness2iCommon.begin()->second;
1985         }
1986         if ( iCommon >= nbNodes )
1987           iCommon = 0; // something wrong
1988
1989         // fill connectivity of tetrahedra based on a current face
1990         int nbTet = nbNodes - 2;
1991         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1992         {
1993           int faceBaryCenInd;
1994           if ( isHex27 )
1995           {
1996             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1997             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1998           }
1999           else
2000           {
2001             method._faceBaryNode[ iF ] = 0;
2002             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2003           }
2004           nbTet = nbNodes;
2005           for ( int i = 0; i < nbTet; ++i )
2006           {
2007             int i1 = i, i2 = (i+1) % nbNodes;
2008             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2010             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2011             connectivity[ connSize++ ] = faceBaryCenInd;
2012             connectivity[ connSize++ ] = baryCenInd;
2013           }
2014         }
2015         else
2016         {
2017           for ( int i = 0; i < nbTet; ++i )
2018           {
2019             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2020             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2022             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2023             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2024             connectivity[ connSize++ ] = baryCenInd;
2025           }
2026         }
2027         method._nbSplits += nbTet;
2028
2029       } // loop on volume faces
2030
2031       connectivity[ connSize++ ] = -1;
2032
2033     } // end of generic solution
2034
2035     return method;
2036   }
2037   //=======================================================================
2038   /*!
2039    * \brief return TSplitMethod to split haxhedron into prisms
2040    */
2041   //=======================================================================
2042
2043   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2044                                     const int        methodFlags,
2045                                     const int        facetToSplit)
2046   {
2047     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2048     // B, T, L, B, R, F
2049     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2050
2051     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2052     {
2053       static TSplitMethod to4methods[4]; // order BT, LR, FB
2054       if ( to4methods[iF]._nbSplits == 0 )
2055       {
2056         switch ( iF ) {
2057         case 0:
2058           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2059           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2060           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2061           break;
2062         case 1:
2063           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2064           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2065           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2066           break;
2067         case 2:
2068           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2069           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2070           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2071           break;
2072         default: return to4methods[3];
2073         }
2074         to4methods[iF]._nbSplits  = 4;
2075         to4methods[iF]._nbCorners = 6;
2076       }
2077       return to4methods[iF];
2078     }
2079     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2080
2081     TSplitMethod method;
2082
2083     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2084
2085     const int nbVariants = 2, nbSplits = 2;
2086     const int** connVariants = 0;
2087     switch ( iF ) {
2088     case 0: connVariants = theHexTo2Prisms_BT; break;
2089     case 1: connVariants = theHexTo2Prisms_LR; break;
2090     case 2: connVariants = theHexTo2Prisms_FB; break;
2091     default: return method;
2092     }
2093
2094     // look for prisms adjacent via facetToSplit and an opposite one
2095     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2096     {
2097       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2098       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2099       if ( nbNodes != 4 ) return method;
2100
2101       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2102       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2103       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2104       TTriangleFacet* t;
2105       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2106         t = &t012;
2107       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2108         t = &t123;
2109       else
2110         continue;
2111
2112       // there are adjacent prism
2113       for ( int variant = 0; variant < nbVariants; ++variant )
2114       {
2115         // check method compliancy with adjacent prisms,
2116         // the found prism facets must be among facets of prisms described by current method
2117         method._nbSplits     = nbSplits;
2118         method._nbCorners    = 6;
2119         method._connectivity = connVariants[ variant ];
2120         if ( method.hasFacet( *t ))
2121           return method;
2122       }
2123     }
2124
2125     // No adjacent prisms. Select a variant with a best aspect ratio.
2126
2127     double badness[2] = { 0, 0 };
2128     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2129     const SMDS_MeshNode** nodes = vol.GetNodes();
2130     for ( int variant = 0; variant < nbVariants; ++variant )
2131       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2132       {
2133         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2134         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2135
2136         method._connectivity = connVariants[ variant ];
2137         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2138         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2139         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2140
2141         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2142                                 nodes[ t->_n2 ],
2143                                 nodes[ t->_n3 ] );
2144         badness[ variant ] += getBadRate( &tria, aspectRatio );
2145       }
2146     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2147
2148     method._nbSplits     = nbSplits;
2149     method._nbCorners    = 6;
2150     method._connectivity = connVariants[ iBetter ];
2151
2152     return method;
2153   }
2154
2155   //================================================================================
2156   /*!
2157    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2158    */
2159   //================================================================================
2160
2161   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2162                                        const SMDSAbs_GeometryType geom ) const
2163   {
2164     // find the tetrahedron including the three nodes of facet
2165     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2166     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2167     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2168     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2169     while ( volIt1->more() )
2170     {
2171       const SMDS_MeshElement* v = volIt1->next();
2172       if ( v->GetGeomType() != geom )
2173         continue;
2174       const int lastCornerInd = v->NbCornerNodes() - 1;
2175       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2176         continue; // medium node not allowed
2177       const int ind2 = v->GetNodeIndex( n2 );
2178       if ( ind2 < 0 || lastCornerInd < ind2 )
2179         continue;
2180       const int ind3 = v->GetNodeIndex( n3 );
2181       if ( ind3 < 0 || lastCornerInd < ind3 )
2182         continue;
2183       return true;
2184     }
2185     return false;
2186   }
2187
2188   //=======================================================================
2189   /*!
2190    * \brief A key of a face of volume
2191    */
2192   //=======================================================================
2193
2194   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2195   {
2196     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2197     {
2198       TIDSortedNodeSet sortedNodes;
2199       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2200       int nbNodes = vol.NbFaceNodes( iF );
2201       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2202       for ( int i = 0; i < nbNodes; i += iQ )
2203         sortedNodes.insert( fNodes[i] );
2204       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2205       first.first   = (*(n++))->GetID();
2206       first.second  = (*(n++))->GetID();
2207       second.first  = (*(n++))->GetID();
2208       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2209     }
2210   };
2211 } // namespace
2212
2213 //=======================================================================
2214 //function : SplitVolumes
2215 //purpose  : Split volume elements into tetrahedra or prisms.
2216 //           If facet ID < 0, element is split into tetrahedra,
2217 //           else a hexahedron is split into prisms so that the given facet is
2218 //           split into triangles
2219 //=======================================================================
2220
2221 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2222                                      const int            theMethodFlags)
2223 {
2224   SMDS_VolumeTool    volTool;
2225   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2226   fHelper.ToFixNodeParameters( true );
2227
2228   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2229   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2230
2231   SMESH_SequenceOfElemPtr newNodes, newElems;
2232
2233   // map face of volume to it's baricenrtic node
2234   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2235   double bc[3];
2236   vector<const SMDS_MeshElement* > splitVols;
2237
2238   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2239   for ( ; elem2facet != theElems.end(); ++elem2facet )
2240   {
2241     const SMDS_MeshElement* elem = elem2facet->first;
2242     const int       facetToSplit = elem2facet->second;
2243     if ( elem->GetType() != SMDSAbs_Volume )
2244       continue;
2245     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2246     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2247       continue;
2248
2249     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2250
2251     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2252                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2253                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2254     if ( splitMethod._nbSplits < 1 ) continue;
2255
2256     // find submesh to add new tetras to
2257     if ( !subMesh || !subMesh->Contains( elem ))
2258     {
2259       int shapeID = FindShape( elem );
2260       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2261       subMesh = GetMeshDS()->MeshElements( shapeID );
2262     }
2263     int iQ;
2264     if ( elem->IsQuadratic() )
2265     {
2266       iQ = 2;
2267       // add quadratic links to the helper
2268       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2269       {
2270         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2271         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2272         for ( int iN = 0; iN < nbN; iN += iQ )
2273           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2274       }
2275       helper.SetIsQuadratic( true );
2276     }
2277     else
2278     {
2279       iQ = 1;
2280       helper.SetIsQuadratic( false );
2281     }
2282     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2283                                         volTool.GetNodes() + elem->NbNodes() );
2284     helper.SetElementsOnShape( true );
2285     if ( splitMethod._baryNode )
2286     {
2287       // make a node at barycenter
2288       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2289       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2290       nodes.push_back( gcNode );
2291       newNodes.Append( gcNode );
2292     }
2293     if ( !splitMethod._faceBaryNode.empty() )
2294     {
2295       // make or find baricentric nodes of faces
2296       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2297       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2298       {
2299         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2300           volFace2BaryNode.insert
2301           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2302         if ( !f_n->second )
2303         {
2304           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2305           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2306         }
2307         nodes.push_back( iF_n->second = f_n->second );
2308       }
2309     }
2310
2311     // make new volumes
2312     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2313     const int* volConn = splitMethod._connectivity;
2314     if ( splitMethod._nbCorners == 4 ) // tetra
2315       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2316         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2317                                                             nodes[ volConn[1] ],
2318                                                             nodes[ volConn[2] ],
2319                                                             nodes[ volConn[3] ]));
2320     else // prisms
2321       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323                                                             nodes[ volConn[1] ],
2324                                                             nodes[ volConn[2] ],
2325                                                             nodes[ volConn[3] ],
2326                                                             nodes[ volConn[4] ],
2327                                                             nodes[ volConn[5] ]));
2328
2329     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2330
2331     // Split faces on sides of the split volume
2332
2333     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2334     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2335     {
2336       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2337       if ( nbNodes < 4 ) continue;
2338
2339       // find an existing face
2340       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2341                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2342       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2343                                                                        /*noMedium=*/false))
2344       {
2345         // make triangles
2346         helper.SetElementsOnShape( false );
2347         vector< const SMDS_MeshElement* > triangles;
2348
2349         // find submesh to add new triangles in
2350         if ( !fSubMesh || !fSubMesh->Contains( face ))
2351         {
2352           int shapeID = FindShape( face );
2353           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2354         }
2355         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2356         if ( iF_n != splitMethod._faceBaryNode.end() )
2357         {
2358           const SMDS_MeshNode *baryNode = iF_n->second;
2359           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2360           {
2361             const SMDS_MeshNode* n1 = fNodes[iN];
2362             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2363             const SMDS_MeshNode *n3 = baryNode;
2364             if ( !volTool.IsFaceExternal( iF ))
2365               swap( n2, n3 );
2366             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2367           }
2368           if ( fSubMesh ) // update position of the bary node on geometry
2369           {
2370             if ( subMesh )
2371               subMesh->RemoveNode( baryNode, false );
2372             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2373             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2374             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2375             {
2376               fHelper.SetSubShape( s );
2377               gp_XY uv( 1e100, 1e100 );
2378               double distXYZ[4];
2379               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2380                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2381                    uv.X() < 1e100 )
2382               {
2383                 // node is too far from the surface
2384                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2385                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2386                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2387               }
2388             }
2389           }
2390         }
2391         else
2392         {
2393           // among possible triangles create ones discribed by split method
2394           const int* nInd = volTool.GetFaceNodesIndices( iF );
2395           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2396           int iCom = 0; // common node of triangle faces to split into
2397           list< TTriangleFacet > facets;
2398           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2399           {
2400             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2401                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2402                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2403             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2404                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2405                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2406             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2407             {
2408               facets.push_back( t012 );
2409               facets.push_back( t023 );
2410               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2411                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2412                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2413                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2414               break;
2415             }
2416           }
2417           list< TTriangleFacet >::iterator facet = facets.begin();
2418           if ( facet == facets.end() )
2419             break;
2420           for ( ; facet != facets.end(); ++facet )
2421           {
2422             if ( !volTool.IsFaceExternal( iF ))
2423               swap( facet->_n2, facet->_n3 );
2424             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2425                                                  volNodes[ facet->_n2 ],
2426                                                  volNodes[ facet->_n3 ]));
2427           }
2428         }
2429         for ( size_t i = 0; i < triangles.size(); ++i )
2430         {
2431           if ( !triangles[ i ]) continue;
2432           if ( fSubMesh )
2433             fSubMesh->AddElement( triangles[ i ]);
2434           newElems.Append( triangles[ i ]);
2435         }
2436         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2437         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2438
2439       } // while a face based on facet nodes exists
2440     } // loop on volume faces to split them into triangles
2441
2442     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2443
2444     if ( geomType == SMDSEntity_TriQuad_Hexa )
2445     {
2446       // remove medium nodes that could become free
2447       for ( int i = 20; i < volTool.NbNodes(); ++i )
2448         if ( volNodes[i]->NbInverseElements() == 0 )
2449           GetMeshDS()->RemoveNode( volNodes[i] );
2450     }
2451   } // loop on volumes to split
2452
2453   myLastCreatedNodes = newNodes;
2454   myLastCreatedElems = newElems;
2455 }
2456
2457 //=======================================================================
2458 //function : GetHexaFacetsToSplit
2459 //purpose  : For hexahedra that will be split into prisms, finds facets to
2460 //           split into triangles. Only hexahedra adjacent to the one closest
2461 //           to theFacetNormal.Location() are returned.
2462 //param [in,out] theHexas - the hexahedra
2463 //param [in]     theFacetNormal - facet normal
2464 //param [out]    theFacets - the hexahedra and found facet IDs
2465 //=======================================================================
2466
2467 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2468                                              const gp_Ax1&     theFacetNormal,
2469                                              TFacetOfElem &    theFacets)
2470 {
2471   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2472
2473   // Find a hexa closest to the location of theFacetNormal
2474
2475   const SMDS_MeshElement* startHex;
2476   {
2477     // get SMDS_ElemIteratorPtr on theHexas
2478     typedef const SMDS_MeshElement*                                      TValue;
2479     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2480     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2481     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2482     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2483     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2484       ( new TElemSetIter( theHexas.begin(),
2485                           theHexas.end(),
2486                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2487
2488     SMESH_ElementSearcher* searcher =
2489       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2490
2491     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2492
2493     delete searcher;
2494
2495     if ( !startHex )
2496       throw SALOME_Exception( THIS_METHOD "startHex not found");
2497   }
2498
2499   // Select a facet of startHex by theFacetNormal
2500
2501   SMDS_VolumeTool vTool( startHex );
2502   double norm[3], dot, maxDot = 0;
2503   int facetID = -1;
2504   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2505     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2506     {
2507       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2508       if ( dot > maxDot )
2509       {
2510         facetID = iF;
2511         maxDot = dot;
2512       }
2513     }
2514   if ( facetID < 0 )
2515     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2516
2517   // Fill theFacets starting from facetID of startHex
2518
2519   // facets used for seach of volumes adjacent to already treated ones
2520   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2521   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2522   TFacetMap facetsToCheck;
2523
2524   set<const SMDS_MeshNode*> facetNodes;
2525   const SMDS_MeshElement*   curHex;
2526
2527   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2528
2529   while ( startHex )
2530   {
2531     // move in two directions from startHex via facetID
2532     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2533     {
2534       curHex       = startHex;
2535       int curFacet = facetID;
2536       if ( is2nd ) // do not treat startHex twice
2537       {
2538         vTool.Set( curHex );
2539         if ( vTool.IsFreeFace( curFacet, &curHex ))
2540         {
2541           curHex = 0;
2542         }
2543         else
2544         {
2545           vTool.GetFaceNodes( curFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes );
2548         }
2549       }
2550       while ( curHex )
2551       {
2552         // store a facet to split
2553         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2554         {
2555           theFacets.insert( make_pair( curHex, -1 ));
2556           break;
2557         }
2558         if ( !allHex && !theHexas.count( curHex ))
2559           break;
2560
2561         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2562           theFacets.insert( make_pair( curHex, curFacet ));
2563         if ( !facetIt2isNew.second )
2564           break;
2565
2566         // remember not-to-split facets in facetsToCheck
2567         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2568         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2569         {
2570           if ( iF == curFacet && iF == oppFacet )
2571             continue;
2572           TVolumeFaceKey facetKey ( vTool, iF );
2573           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2574           pair< TFacetMap::iterator, bool > it2isnew =
2575             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2576           if ( !it2isnew.second )
2577             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2578         }
2579         // pass to a volume adjacent via oppFacet
2580         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2581         {
2582           curHex = 0;
2583         }
2584         else
2585         {
2586           // get a new curFacet
2587           vTool.GetFaceNodes( oppFacet, facetNodes );
2588           vTool.Set( curHex );
2589           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2590         }
2591       }
2592     } // move in two directions from startHex via facetID
2593
2594     // Find a new startHex by facetsToCheck
2595
2596     startHex = 0;
2597     facetID  = -1;
2598     TFacetMap::iterator fIt = facetsToCheck.begin();
2599     while ( !startHex && fIt != facetsToCheck.end() )
2600     {
2601       const TElemFacets&  elemFacets = fIt->second;
2602       const SMDS_MeshElement*    hex = elemFacets.first->first;
2603       int                 splitFacet = elemFacets.first->second;
2604       int               lateralFacet = elemFacets.second;
2605       facetsToCheck.erase( fIt );
2606       fIt = facetsToCheck.begin();
2607
2608       vTool.Set( hex );
2609       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2610            curHex->GetGeomType() != SMDSGeom_HEXA )
2611         continue;
2612       if ( !allHex && !theHexas.count( curHex ))
2613         continue;
2614
2615       startHex = curHex;
2616
2617       // find a facet of startHex to split
2618
2619       set<const SMDS_MeshNode*> lateralNodes;
2620       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2621       vTool.GetFaceNodes( splitFacet,   facetNodes );
2622       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2623       vTool.Set( startHex );
2624       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2625
2626       // look for a facet of startHex having common nodes with facetNodes
2627       // but not lateralFacet
2628       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2629       {
2630         if ( iF == lateralFacet )
2631           continue;
2632         int nbCommonNodes = 0;
2633         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2634         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2635           nbCommonNodes += facetNodes.count( nn[ iN ]);
2636
2637         if ( nbCommonNodes >= 2 )
2638         {
2639           facetID = iF;
2640           break;
2641         }
2642       }
2643       if ( facetID < 0 )
2644         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2645     }
2646   } //   while ( startHex )
2647
2648   return;
2649 }
2650
2651 namespace
2652 {
2653   //================================================================================
2654   /*!
2655    * \brief Selects nodes of several elements according to a given interlace
2656    *  \param [in] srcNodes - nodes to select from
2657    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2658    *  \param [in] interlace - indices of nodes for all elements
2659    *  \param [in] nbElems - nb of elements
2660    *  \param [in] nbNodes - nb of nodes in each element
2661    *  \param [in] mesh - the mesh
2662    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2663    *  \param [in] type - type of elements to look for
2664    */
2665   //================================================================================
2666
2667   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2668                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2669                     const int*                            interlace,
2670                     const int                             nbElems,
2671                     const int                             nbNodes,
2672                     SMESHDS_Mesh*                         mesh = 0,
2673                     list< const SMDS_MeshElement* >*      elemQueue=0,
2674                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2675   {
2676     for ( int iE = 0; iE < nbElems; ++iE )
2677     {
2678       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2679       const int*                         select = & interlace[iE*nbNodes];
2680       elemNodes.resize( nbNodes );
2681       for ( int iN = 0; iN < nbNodes; ++iN )
2682         elemNodes[iN] = srcNodes[ select[ iN ]];
2683     }
2684     const SMDS_MeshElement* e;
2685     if ( elemQueue )
2686       for ( int iE = 0; iE < nbElems; ++iE )
2687         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2688           elemQueue->push_back( e );
2689   }
2690 }
2691
2692 //=======================================================================
2693 /*
2694  * Split bi-quadratic elements into linear ones without creation of additional nodes
2695  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2696  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2697  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2698  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2699  *   will be split in order to keep the mesh conformal.
2700  *  \param elems - elements to split
2701  */
2702 //=======================================================================
2703
2704 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2705 {
2706   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2707   vector<const SMDS_MeshElement* > splitElems;
2708   list< const SMDS_MeshElement* > elemQueue;
2709   list< const SMDS_MeshElement* >::iterator elemIt;
2710
2711   SMESHDS_Mesh * mesh = GetMeshDS();
2712   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2713   int nbElems, nbNodes;
2714
2715   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2716   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2717   {
2718     elemQueue.clear();
2719     elemQueue.push_back( *elemSetIt );
2720     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2721     {
2722       const SMDS_MeshElement* elem = *elemIt;
2723       switch( elem->GetEntityType() )
2724       {
2725       case SMDSEntity_TriQuad_Hexa: // HEX27
2726       {
2727         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2728         nbElems  = nbNodes = 8;
2729         elemType = & hexaType;
2730
2731         // get nodes for new elements
2732         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2733                                  { 1,9,20,8,    17,22,26,21 },
2734                                  { 2,10,20,9,   18,23,26,22 },
2735                                  { 3,11,20,10,  19,24,26,23 },
2736                                  { 16,21,26,24, 4,12,25,15  },
2737                                  { 17,22,26,21, 5,13,25,12  },
2738                                  { 18,23,26,22, 6,14,25,13  },
2739                                  { 19,24,26,23, 7,15,25,14  }};
2740         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2741
2742         // add boundary faces to elemQueue
2743         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2744                                  { 4,5,6,7, 12,13,14,15, 25 },
2745                                  { 0,1,5,4, 8,17,12,16,  21 },
2746                                  { 1,2,6,5, 9,18,13,17,  22 },
2747                                  { 2,3,7,6, 10,19,14,18, 23 },
2748                                  { 3,0,4,7, 11,16,15,19, 24 }};
2749         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2750
2751         // add boundary segments to elemQueue
2752         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2753                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2754                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2755         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2756         break;
2757       }
2758       case SMDSEntity_BiQuad_Triangle: // TRIA7
2759       {
2760         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2761         nbElems = 3;
2762         nbNodes = 4;
2763         elemType = & quadType;
2764
2765         // get nodes for new elements
2766         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2767         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2768
2769         // add boundary segments to elemQueue
2770         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2771         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2772         break;
2773       }
2774       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2775       {
2776         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2777         nbElems = 4;
2778         nbNodes = 4;
2779         elemType = & quadType;
2780
2781         // get nodes for new elements
2782         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2783         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2784
2785         // add boundary segments to elemQueue
2786         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2787         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2788         break;
2789       }
2790       case SMDSEntity_Quad_Edge:
2791       {
2792         if ( elemIt == elemQueue.begin() )
2793           continue; // an elem is in theElems
2794         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2795         nbElems = 2;
2796         nbNodes = 2;
2797         elemType = & segType;
2798
2799         // get nodes for new elements
2800         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2801         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2802         break;
2803       }
2804       default: continue;
2805       } // switch( elem->GetEntityType() )
2806
2807       // Create new elements
2808
2809       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2810
2811       splitElems.clear();
2812
2813       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2814       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2815       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2816       //elemType->SetID( -1 );
2817
2818       for ( int iE = 0; iE < nbElems; ++iE )
2819         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2820
2821
2822       ReplaceElemInGroups( elem, splitElems, mesh );
2823
2824       if ( subMesh )
2825         for ( size_t i = 0; i < splitElems.size(); ++i )
2826           subMesh->AddElement( splitElems[i] );
2827     }
2828   }
2829 }
2830
2831 //=======================================================================
2832 //function : AddToSameGroups
2833 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2834 //=======================================================================
2835
2836 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2837                                         const SMDS_MeshElement* elemInGroups,
2838                                         SMESHDS_Mesh *          aMesh)
2839 {
2840   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2841   if (!groups.empty()) {
2842     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2843     for ( ; grIt != groups.end(); grIt++ ) {
2844       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2845       if ( group && group->Contains( elemInGroups ))
2846         group->SMDSGroup().Add( elemToAdd );
2847     }
2848   }
2849 }
2850
2851
2852 //=======================================================================
2853 //function : RemoveElemFromGroups
2854 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2855 //=======================================================================
2856 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2857                                              SMESHDS_Mesh *          aMesh)
2858 {
2859   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2860   if (!groups.empty())
2861   {
2862     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2863     for (; GrIt != groups.end(); GrIt++)
2864     {
2865       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2866       if (!grp || grp->IsEmpty()) continue;
2867       grp->SMDSGroup().Remove(removeelem);
2868     }
2869   }
2870 }
2871
2872 //================================================================================
2873 /*!
2874  * \brief Replace elemToRm by elemToAdd in the all groups
2875  */
2876 //================================================================================
2877
2878 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2879                                             const SMDS_MeshElement* elemToAdd,
2880                                             SMESHDS_Mesh *          aMesh)
2881 {
2882   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2883   if (!groups.empty()) {
2884     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2885     for ( ; grIt != groups.end(); grIt++ ) {
2886       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2887       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2888         group->SMDSGroup().Add( elemToAdd );
2889     }
2890   }
2891 }
2892
2893 //================================================================================
2894 /*!
2895  * \brief Replace elemToRm by elemToAdd in the all groups
2896  */
2897 //================================================================================
2898
2899 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2900                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2901                                             SMESHDS_Mesh *                         aMesh)
2902 {
2903   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2904   if (!groups.empty())
2905   {
2906     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2907     for ( ; grIt != groups.end(); grIt++ ) {
2908       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2909       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2910         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2911           group->SMDSGroup().Add( elemToAdd[ i ] );
2912     }
2913   }
2914 }
2915
2916 //=======================================================================
2917 //function : QuadToTri
2918 //purpose  : Cut quadrangles into triangles.
2919 //           theCrit is used to select a diagonal to cut
2920 //=======================================================================
2921
2922 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2923                                   const bool         the13Diag)
2924 {
2925   myLastCreatedElems.Clear();
2926   myLastCreatedNodes.Clear();
2927
2928   SMESHDS_Mesh * aMesh = GetMeshDS();
2929
2930   Handle(Geom_Surface) surface;
2931   SMESH_MesherHelper   helper( *GetMesh() );
2932
2933   TIDSortedElemSet::iterator itElem;
2934   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2935   {
2936     const SMDS_MeshElement* elem = *itElem;
2937     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2938       continue;
2939
2940     if ( elem->NbNodes() == 4 ) {
2941       // retrieve element nodes
2942       const SMDS_MeshNode* aNodes [4];
2943       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2944       int i = 0;
2945       while ( itN->more() )
2946         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2947
2948       int aShapeId = FindShape( elem );
2949       const SMDS_MeshElement* newElem1 = 0;
2950       const SMDS_MeshElement* newElem2 = 0;
2951       if ( the13Diag ) {
2952         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2953         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2954       }
2955       else {
2956         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2957         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2958       }
2959       myLastCreatedElems.Append(newElem1);
2960       myLastCreatedElems.Append(newElem2);
2961       // put a new triangle on the same shape and add to the same groups
2962       if ( aShapeId )
2963       {
2964         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2965         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2966       }
2967       AddToSameGroups( newElem1, elem, aMesh );
2968       AddToSameGroups( newElem2, elem, aMesh );
2969       aMesh->RemoveElement( elem );
2970     }
2971
2972     // Quadratic quadrangle
2973
2974     else if ( elem->NbNodes() >= 8 )
2975     {
2976       // get surface elem is on
2977       int aShapeId = FindShape( elem );
2978       if ( aShapeId != helper.GetSubShapeID() ) {
2979         surface.Nullify();
2980         TopoDS_Shape shape;
2981         if ( aShapeId > 0 )
2982           shape = aMesh->IndexToShape( aShapeId );
2983         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2984           TopoDS_Face face = TopoDS::Face( shape );
2985           surface = BRep_Tool::Surface( face );
2986           if ( !surface.IsNull() )
2987             helper.SetSubShape( shape );
2988         }
2989       }
2990
2991       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2992       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2993       for ( int i = 0; itN->more(); ++i )
2994         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2995
2996       const SMDS_MeshNode* centrNode = aNodes[8];
2997       if ( centrNode == 0 )
2998       {
2999         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3000                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3001                                            surface.IsNull() );
3002         myLastCreatedNodes.Append(centrNode);
3003       }
3004
3005       // create a new element
3006       const SMDS_MeshElement* newElem1 = 0;
3007       const SMDS_MeshElement* newElem2 = 0;
3008       if ( the13Diag ) {
3009         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3010                                   aNodes[6], aNodes[7], centrNode );
3011         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3012                                   centrNode, aNodes[4], aNodes[5] );
3013       }
3014       else {
3015         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3016                                   aNodes[7], aNodes[4], centrNode );
3017         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3018                                   centrNode, aNodes[5], aNodes[6] );
3019       }
3020       myLastCreatedElems.Append(newElem1);
3021       myLastCreatedElems.Append(newElem2);
3022       // put a new triangle on the same shape and add to the same groups
3023       if ( aShapeId )
3024       {
3025         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3026         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3027       }
3028       AddToSameGroups( newElem1, elem, aMesh );
3029       AddToSameGroups( newElem2, elem, aMesh );
3030       aMesh->RemoveElement( elem );
3031     }
3032   }
3033
3034   return true;
3035 }
3036
3037 //=======================================================================
3038 //function : getAngle
3039 //purpose  :
3040 //=======================================================================
3041
3042 double getAngle(const SMDS_MeshElement * tr1,
3043                 const SMDS_MeshElement * tr2,
3044                 const SMDS_MeshNode *    n1,
3045                 const SMDS_MeshNode *    n2)
3046 {
3047   double angle = 2. * M_PI; // bad angle
3048
3049   // get normals
3050   SMESH::Controls::TSequenceOfXYZ P1, P2;
3051   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3052        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3053     return angle;
3054   gp_Vec N1,N2;
3055   if(!tr1->IsQuadratic())
3056     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3057   else
3058     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3059   if ( N1.SquareMagnitude() <= gp::Resolution() )
3060     return angle;
3061   if(!tr2->IsQuadratic())
3062     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3063   else
3064     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3065   if ( N2.SquareMagnitude() <= gp::Resolution() )
3066     return angle;
3067
3068   // find the first diagonal node n1 in the triangles:
3069   // take in account a diagonal link orientation
3070   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3071   for ( int t = 0; t < 2; t++ ) {
3072     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3073     int i = 0, iDiag = -1;
3074     while ( it->more()) {
3075       const SMDS_MeshElement *n = it->next();
3076       if ( n == n1 || n == n2 ) {
3077         if ( iDiag < 0)
3078           iDiag = i;
3079         else {
3080           if ( i - iDiag == 1 )
3081             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3082           else
3083             nFirst[ t ] = n;
3084           break;
3085         }
3086       }
3087       i++;
3088     }
3089   }
3090   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3091     N2.Reverse();
3092
3093   angle = N1.Angle( N2 );
3094   //SCRUTE( angle );
3095   return angle;
3096 }
3097
3098 // =================================================
3099 // class generating a unique ID for a pair of nodes
3100 // and able to return nodes by that ID
3101 // =================================================
3102 class LinkID_Gen {
3103 public:
3104
3105   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3106     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3107   {}
3108
3109   long GetLinkID (const SMDS_MeshNode * n1,
3110                   const SMDS_MeshNode * n2) const
3111   {
3112     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3113   }
3114
3115   bool GetNodes (const long             theLinkID,
3116                  const SMDS_MeshNode* & theNode1,
3117                  const SMDS_MeshNode* & theNode2) const
3118   {
3119     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3120     if ( !theNode1 ) return false;
3121     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3122     if ( !theNode2 ) return false;
3123     return true;
3124   }
3125
3126 private:
3127   LinkID_Gen();
3128   const SMESHDS_Mesh* myMesh;
3129   long                myMaxID;
3130 };
3131
3132
3133 //=======================================================================
3134 //function : TriToQuad
3135 //purpose  : Fuse neighbour triangles into quadrangles.
3136 //           theCrit is used to select a neighbour to fuse with.
3137 //           theMaxAngle is a max angle between element normals at which
3138 //           fusion is still performed.
3139 //=======================================================================
3140
3141 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3142                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3143                                   const double                         theMaxAngle)
3144 {
3145   myLastCreatedElems.Clear();
3146   myLastCreatedNodes.Clear();
3147
3148   if ( !theCrit.get() )
3149     return false;
3150
3151   SMESHDS_Mesh * aMesh = GetMeshDS();
3152
3153   // Prepare data for algo: build
3154   // 1. map of elements with their linkIDs
3155   // 2. map of linkIDs with their elements
3156
3157   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3158   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3159   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3160   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3161
3162   TIDSortedElemSet::iterator itElem;
3163   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3164   {
3165     const SMDS_MeshElement* elem = *itElem;
3166     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3167     bool IsTria = ( elem->NbCornerNodes()==3 );
3168     if (!IsTria) continue;
3169
3170     // retrieve element nodes
3171     const SMDS_MeshNode* aNodes [4];
3172     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3173     int i = 0;
3174     while ( i < 3 )
3175       aNodes[ i++ ] = itN->next();
3176     aNodes[ 3 ] = aNodes[ 0 ];
3177
3178     // fill maps
3179     for ( i = 0; i < 3; i++ ) {
3180       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3181       // check if elements sharing a link can be fused
3182       itLE = mapLi_listEl.find( link );
3183       if ( itLE != mapLi_listEl.end() ) {
3184         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3185           continue;
3186         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3187         //if ( FindShape( elem ) != FindShape( elem2 ))
3188         //  continue; // do not fuse triangles laying on different shapes
3189         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3190           continue; // avoid making badly shaped quads
3191         (*itLE).second.push_back( elem );
3192       }
3193       else {
3194         mapLi_listEl[ link ].push_back( elem );
3195       }
3196       mapEl_setLi [ elem ].insert( link );
3197     }
3198   }
3199   // Clean the maps from the links shared by a sole element, ie
3200   // links to which only one element is bound in mapLi_listEl
3201
3202   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3203     int nbElems = (*itLE).second.size();
3204     if ( nbElems < 2  ) {
3205       const SMDS_MeshElement* elem = (*itLE).second.front();
3206       SMESH_TLink link = (*itLE).first;
3207       mapEl_setLi[ elem ].erase( link );
3208       if ( mapEl_setLi[ elem ].empty() )
3209         mapEl_setLi.erase( elem );
3210     }
3211   }
3212
3213   // Algo: fuse triangles into quadrangles
3214
3215   while ( ! mapEl_setLi.empty() ) {
3216     // Look for the start element:
3217     // the element having the least nb of shared links
3218     const SMDS_MeshElement* startElem = 0;
3219     int minNbLinks = 4;
3220     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3221       int nbLinks = (*itEL).second.size();
3222       if ( nbLinks < minNbLinks ) {
3223         startElem = (*itEL).first;
3224         minNbLinks = nbLinks;
3225         if ( minNbLinks == 1 )
3226           break;
3227       }
3228     }
3229
3230     // search elements to fuse starting from startElem or links of elements
3231     // fused earlyer - startLinks
3232     list< SMESH_TLink > startLinks;
3233     while ( startElem || !startLinks.empty() ) {
3234       while ( !startElem && !startLinks.empty() ) {
3235         // Get an element to start, by a link
3236         SMESH_TLink linkId = startLinks.front();
3237         startLinks.pop_front();
3238         itLE = mapLi_listEl.find( linkId );
3239         if ( itLE != mapLi_listEl.end() ) {
3240           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3241           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3242           for ( ; itE != listElem.end() ; itE++ )
3243             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3244               startElem = (*itE);
3245           mapLi_listEl.erase( itLE );
3246         }
3247       }
3248
3249       if ( startElem ) {
3250         // Get candidates to be fused
3251         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3252         const SMESH_TLink *link12 = 0, *link13 = 0;
3253         startElem = 0;
3254         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3255         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3256         ASSERT( !setLi.empty() );
3257         set< SMESH_TLink >::iterator itLi;
3258         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3259         {
3260           const SMESH_TLink & link = (*itLi);
3261           itLE = mapLi_listEl.find( link );
3262           if ( itLE == mapLi_listEl.end() )
3263             continue;
3264
3265           const SMDS_MeshElement* elem = (*itLE).second.front();
3266           if ( elem == tr1 )
3267             elem = (*itLE).second.back();
3268           mapLi_listEl.erase( itLE );
3269           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3270             continue;
3271           if ( tr2 ) {
3272             tr3 = elem;
3273             link13 = &link;
3274           }
3275           else {
3276             tr2 = elem;
3277             link12 = &link;
3278           }
3279
3280           // add other links of elem to list of links to re-start from
3281           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3282           set< SMESH_TLink >::iterator it;
3283           for ( it = links.begin(); it != links.end(); it++ ) {
3284             const SMESH_TLink& link2 = (*it);
3285             if ( link2 != link )
3286               startLinks.push_back( link2 );
3287           }
3288         }
3289
3290         // Get nodes of possible quadrangles
3291         const SMDS_MeshNode *n12 [4], *n13 [4];
3292         bool Ok12 = false, Ok13 = false;
3293         const SMDS_MeshNode *linkNode1, *linkNode2;
3294         if(tr2) {
3295           linkNode1 = link12->first;
3296           linkNode2 = link12->second;
3297           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3298             Ok12 = true;
3299         }
3300         if(tr3) {
3301           linkNode1 = link13->first;
3302           linkNode2 = link13->second;
3303           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3304             Ok13 = true;
3305         }
3306
3307         // Choose a pair to fuse
3308         if ( Ok12 && Ok13 ) {
3309           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3310           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3311           double aBadRate12 = getBadRate( &quad12, theCrit );
3312           double aBadRate13 = getBadRate( &quad13, theCrit );
3313           if (  aBadRate13 < aBadRate12 )
3314             Ok12 = false;
3315           else
3316             Ok13 = false;
3317         }
3318
3319         // Make quadrangles
3320         // and remove fused elems and remove links from the maps
3321         mapEl_setLi.erase( tr1 );
3322         if ( Ok12 )
3323         {
3324           mapEl_setLi.erase( tr2 );
3325           mapLi_listEl.erase( *link12 );
3326           if ( tr1->NbNodes() == 3 )
3327           {
3328             const SMDS_MeshElement* newElem = 0;
3329             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3330             myLastCreatedElems.Append(newElem);
3331             AddToSameGroups( newElem, tr1, aMesh );
3332             int aShapeId = tr1->getshapeId();
3333             if ( aShapeId )
3334               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3335             aMesh->RemoveElement( tr1 );
3336             aMesh->RemoveElement( tr2 );
3337           }
3338           else {
3339             vector< const SMDS_MeshNode* > N1;
3340             vector< const SMDS_MeshNode* > N2;
3341             getNodesFromTwoTria(tr1,tr2,N1,N2);
3342             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3343             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3344             // i.e. first nodes from both arrays form a new diagonal
3345             const SMDS_MeshNode* aNodes[8];
3346             aNodes[0] = N1[0];
3347             aNodes[1] = N1[1];
3348             aNodes[2] = N2[0];
3349             aNodes[3] = N2[1];
3350             aNodes[4] = N1[3];
3351             aNodes[5] = N2[5];
3352             aNodes[6] = N2[3];
3353             aNodes[7] = N1[5];
3354             const SMDS_MeshElement* newElem = 0;
3355             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3356               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3358             else
3359               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3360                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3361             myLastCreatedElems.Append(newElem);
3362             AddToSameGroups( newElem, tr1, aMesh );
3363             int aShapeId = tr1->getshapeId();
3364             if ( aShapeId )
3365               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3366             aMesh->RemoveElement( tr1 );
3367             aMesh->RemoveElement( tr2 );
3368             // remove middle node (9)
3369             if ( N1[4]->NbInverseElements() == 0 )
3370               aMesh->RemoveNode( N1[4] );
3371             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3372               aMesh->RemoveNode( N1[6] );
3373             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3374               aMesh->RemoveNode( N2[6] );
3375           }
3376         }
3377         else if ( Ok13 )
3378         {
3379           mapEl_setLi.erase( tr3 );
3380           mapLi_listEl.erase( *link13 );
3381           if ( tr1->NbNodes() == 3 ) {
3382             const SMDS_MeshElement* newElem = 0;
3383             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3384             myLastCreatedElems.Append(newElem);
3385             AddToSameGroups( newElem, tr1, aMesh );
3386             int aShapeId = tr1->getshapeId();
3387             if ( aShapeId )
3388               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3389             aMesh->RemoveElement( tr1 );
3390             aMesh->RemoveElement( tr3 );
3391           }
3392           else {
3393             vector< const SMDS_MeshNode* > N1;
3394             vector< const SMDS_MeshNode* > N2;
3395             getNodesFromTwoTria(tr1,tr3,N1,N2);
3396             // now we receive following N1 and N2 (using numeration as above image)
3397             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3398             // i.e. first nodes from both arrays form a new diagonal
3399             const SMDS_MeshNode* aNodes[8];
3400             aNodes[0] = N1[0];
3401             aNodes[1] = N1[1];
3402             aNodes[2] = N2[0];
3403             aNodes[3] = N2[1];
3404             aNodes[4] = N1[3];
3405             aNodes[5] = N2[5];
3406             aNodes[6] = N2[3];
3407             aNodes[7] = N1[5];
3408             const SMDS_MeshElement* newElem = 0;
3409             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3410               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3411                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3412             else
3413               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3414                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3415             myLastCreatedElems.Append(newElem);
3416             AddToSameGroups( newElem, tr1, aMesh );
3417             int aShapeId = tr1->getshapeId();
3418             if ( aShapeId )
3419               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3420             aMesh->RemoveElement( tr1 );
3421             aMesh->RemoveElement( tr3 );
3422             // remove middle node (9)
3423             if ( N1[4]->NbInverseElements() == 0 )
3424               aMesh->RemoveNode( N1[4] );
3425             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3426               aMesh->RemoveNode( N1[6] );
3427             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3428               aMesh->RemoveNode( N2[6] );
3429           }
3430         }
3431
3432         // Next element to fuse: the rejected one
3433         if ( tr3 )
3434           startElem = Ok12 ? tr3 : tr2;
3435
3436       } // if ( startElem )
3437     } // while ( startElem || !startLinks.empty() )
3438   } // while ( ! mapEl_setLi.empty() )
3439
3440   return true;
3441 }
3442
3443
3444 /*#define DUMPSO(txt) \
3445 //  cout << txt << endl;
3446 //=============================================================================
3447 //
3448 //
3449 //
3450 //=============================================================================
3451 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3452 {
3453 if ( i1 == i2 )
3454 return;
3455 int tmp = idNodes[ i1 ];
3456 idNodes[ i1 ] = idNodes[ i2 ];
3457 idNodes[ i2 ] = tmp;
3458 gp_Pnt Ptmp = P[ i1 ];
3459 P[ i1 ] = P[ i2 ];
3460 P[ i2 ] = Ptmp;
3461 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3462 }
3463
3464 //=======================================================================
3465 //function : SortQuadNodes
3466 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3467 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3468 //           1 or 2 else 0.
3469 //=======================================================================
3470
3471 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3472 int               idNodes[] )
3473 {
3474   gp_Pnt P[4];
3475   int i;
3476   for ( i = 0; i < 4; i++ ) {
3477     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3478     if ( !n ) return 0;
3479     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3480   }
3481
3482   gp_Vec V1(P[0], P[1]);
3483   gp_Vec V2(P[0], P[2]);
3484   gp_Vec V3(P[0], P[3]);
3485
3486   gp_Vec Cross1 = V1 ^ V2;
3487   gp_Vec Cross2 = V2 ^ V3;
3488
3489   i = 0;
3490   if (Cross1.Dot(Cross2) < 0)
3491   {
3492     Cross1 = V2 ^ V1;
3493     Cross2 = V1 ^ V3;
3494
3495     if (Cross1.Dot(Cross2) < 0)
3496       i = 2;
3497     else
3498       i = 1;
3499     swap ( i, i + 1, idNodes, P );
3500
3501     //     for ( int ii = 0; ii < 4; ii++ ) {
3502     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3503     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3504     //     }
3505   }
3506   return i;
3507 }
3508
3509 //=======================================================================
3510 //function : SortHexaNodes
3511 //purpose  : Set 8 nodes of a hexahedron in a good order.
3512 //           Return success status
3513 //=======================================================================
3514
3515 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3516                                       int               idNodes[] )
3517 {
3518   gp_Pnt P[8];
3519   int i;
3520   DUMPSO( "INPUT: ========================================");
3521   for ( i = 0; i < 8; i++ ) {
3522     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3523     if ( !n ) return false;
3524     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3525     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3526   }
3527   DUMPSO( "========================================");
3528
3529
3530   set<int> faceNodes;  // ids of bottom face nodes, to be found
3531   set<int> checkedId1; // ids of tried 2-nd nodes
3532   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3533   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3534   int iMin, iLoop1 = 0;
3535
3536   // Loop to try the 2-nd nodes
3537
3538   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3539   {
3540     // Find not checked 2-nd node
3541     for ( i = 1; i < 8; i++ )
3542       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3543         int id1 = idNodes[i];
3544         swap ( 1, i, idNodes, P );
3545         checkedId1.insert ( id1 );
3546         break;
3547       }
3548
3549     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3550     // ie that all but meybe one (id3 which is on the same face) nodes
3551     // lay on the same side from the triangle plane.
3552
3553     bool manyInPlane = false; // more than 4 nodes lay in plane
3554     int iLoop2 = 0;
3555     while ( ++iLoop2 < 6 ) {
3556
3557       // get 1-2-3 plane coeffs
3558       Standard_Real A, B, C, D;
3559       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3560       if ( N.SquareMagnitude() > gp::Resolution() )
3561       {
3562         gp_Pln pln ( P[0], N );
3563         pln.Coefficients( A, B, C, D );
3564
3565         // find the node (iMin) closest to pln
3566         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3567         set<int> idInPln;
3568         for ( i = 3; i < 8; i++ ) {
3569           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3570           if ( fabs( dist[i] ) < minDist ) {
3571             minDist = fabs( dist[i] );
3572             iMin = i;
3573           }
3574           if ( fabs( dist[i] ) <= tol )
3575             idInPln.insert( idNodes[i] );
3576         }
3577
3578         // there should not be more than 4 nodes in bottom plane
3579         if ( idInPln.size() > 1 )
3580         {
3581           DUMPSO( "### idInPln.size() = " << idInPln.size());
3582           // idInPlane does not contain the first 3 nodes
3583           if ( manyInPlane || idInPln.size() == 5)
3584             return false; // all nodes in one plane
3585           manyInPlane = true;
3586
3587           // set the 1-st node to be not in plane
3588           for ( i = 3; i < 8; i++ ) {
3589             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3590               DUMPSO( "### Reset 0-th node");
3591               swap( 0, i, idNodes, P );
3592               break;
3593             }
3594           }
3595
3596           // reset to re-check second nodes
3597           leastDist = DBL_MAX;
3598           faceNodes.clear();
3599           checkedId1.clear();
3600           iLoop1 = 0;
3601           break; // from iLoop2;
3602         }
3603
3604         // check that the other 4 nodes are on the same side
3605         bool sameSide = true;
3606         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3607         for ( i = 3; sameSide && i < 8; i++ ) {
3608           if ( i != iMin )
3609             sameSide = ( isNeg == dist[i] <= 0.);
3610         }
3611
3612         // keep best solution
3613         if ( sameSide && minDist < leastDist ) {
3614           leastDist = minDist;
3615           faceNodes.clear();
3616           faceNodes.insert( idNodes[ 1 ] );
3617           faceNodes.insert( idNodes[ 2 ] );
3618           faceNodes.insert( idNodes[ iMin ] );
3619           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3620                   << " leastDist = " << leastDist);
3621           if ( leastDist <= DBL_MIN )
3622             break;
3623         }
3624       }
3625
3626       // set next 3-d node to check
3627       int iNext = 2 + iLoop2;
3628       if ( iNext < 8 ) {
3629         DUMPSO( "Try 2-nd");
3630         swap ( 2, iNext, idNodes, P );
3631       }
3632     } // while ( iLoop2 < 6 )
3633   } // iLoop1
3634
3635   if ( faceNodes.empty() ) return false;
3636
3637   // Put the faceNodes in proper places
3638   for ( i = 4; i < 8; i++ ) {
3639     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3640       // find a place to put
3641       int iTo = 1;
3642       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3643         iTo++;
3644       DUMPSO( "Set faceNodes");
3645       swap ( iTo, i, idNodes, P );
3646     }
3647   }
3648
3649
3650   // Set nodes of the found bottom face in good order
3651   DUMPSO( " Found bottom face: ");
3652   i = SortQuadNodes( theMesh, idNodes );
3653   if ( i ) {
3654     gp_Pnt Ptmp = P[ i ];
3655     P[ i ] = P[ i+1 ];
3656     P[ i+1 ] = Ptmp;
3657   }
3658   //   else
3659   //     for ( int ii = 0; ii < 4; ii++ ) {
3660   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3661   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3662   //    }
3663
3664   // Gravity center of the top and bottom faces
3665   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3666   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3667
3668   // Get direction from the bottom to the top face
3669   gp_Vec upDir ( aGCb, aGCt );
3670   Standard_Real upDirSize = upDir.Magnitude();
3671   if ( upDirSize <= gp::Resolution() ) return false;
3672   upDir / upDirSize;
3673
3674   // Assure that the bottom face normal points up
3675   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3676   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3677   if ( Nb.Dot( upDir ) < 0 ) {
3678     DUMPSO( "Reverse bottom face");
3679     swap( 1, 3, idNodes, P );
3680   }
3681
3682   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3683   Standard_Real minDist = DBL_MAX;
3684   for ( i = 4; i < 8; i++ ) {
3685     // projection of P[i] to the plane defined by P[0] and upDir
3686     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3687     Standard_Real sqDist = P[0].SquareDistance( Pp );
3688     if ( sqDist < minDist ) {
3689       minDist = sqDist;
3690       iMin = i;
3691     }
3692   }
3693   DUMPSO( "Set 4-th");
3694   swap ( 4, iMin, idNodes, P );
3695
3696   // Set nodes of the top face in good order
3697   DUMPSO( "Sort top face");
3698   i = SortQuadNodes( theMesh, &idNodes[4] );
3699   if ( i ) {
3700     i += 4;
3701     gp_Pnt Ptmp = P[ i ];
3702     P[ i ] = P[ i+1 ];
3703     P[ i+1 ] = Ptmp;
3704   }
3705
3706   // Assure that direction of the top face normal is from the bottom face
3707   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3708   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3709   if ( Nt.Dot( upDir ) < 0 ) {
3710     DUMPSO( "Reverse top face");
3711     swap( 5, 7, idNodes, P );
3712   }
3713
3714   //   DUMPSO( "OUTPUT: ========================================");
3715   //   for ( i = 0; i < 8; i++ ) {
3716   //     float *p = ugrid->GetPoint(idNodes[i]);
3717   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3718   //   }
3719
3720   return true;
3721 }*/
3722
3723 //================================================================================
3724 /*!
3725  * \brief Return nodes linked to the given one
3726  * \param theNode - the node
3727  * \param linkedNodes - the found nodes
3728  * \param type - the type of elements to check
3729  *
3730  * Medium nodes are ignored
3731  */
3732 //================================================================================
3733
3734 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3735                                        TIDSortedElemSet &   linkedNodes,
3736                                        SMDSAbs_ElementType  type )
3737 {
3738   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3739   while ( elemIt->more() )
3740   {
3741     const SMDS_MeshElement* elem = elemIt->next();
3742     if(elem->GetType() == SMDSAbs_0DElement)
3743       continue;
3744
3745     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3746     if ( elem->GetType() == SMDSAbs_Volume )
3747     {
3748       SMDS_VolumeTool vol( elem );
3749       while ( nodeIt->more() ) {
3750         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3751         if ( theNode != n && vol.IsLinked( theNode, n ))
3752           linkedNodes.insert( n );
3753       }
3754     }
3755     else
3756     {
3757       for ( int i = 0; nodeIt->more(); ++i ) {
3758         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759         if ( n == theNode ) {
3760           int iBefore = i - 1;
3761           int iAfter  = i + 1;
3762           if ( elem->IsQuadratic() ) {
3763             int nb = elem->NbNodes() / 2;
3764             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3765             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3766           }
3767           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3768           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3769         }
3770       }
3771     }
3772   }
3773 }
3774
3775 //=======================================================================
3776 //function : laplacianSmooth
3777 //purpose  : pulls theNode toward the center of surrounding nodes directly
3778 //           connected to that node along an element edge
3779 //=======================================================================
3780
3781 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3782                      const Handle(Geom_Surface)&          theSurface,
3783                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3784 {
3785   // find surrounding nodes
3786
3787   TIDSortedElemSet nodeSet;
3788   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3789
3790   // compute new coodrs
3791
3792   double coord[] = { 0., 0., 0. };
3793   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3794   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3795     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3796     if ( theSurface.IsNull() ) { // smooth in 3D
3797       coord[0] += node->X();
3798       coord[1] += node->Y();
3799       coord[2] += node->Z();
3800     }
3801     else { // smooth in 2D
3802       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3803       gp_XY* uv = theUVMap[ node ];
3804       coord[0] += uv->X();
3805       coord[1] += uv->Y();
3806     }
3807   }
3808   int nbNodes = nodeSet.size();
3809   if ( !nbNodes )
3810     return;
3811   coord[0] /= nbNodes;
3812   coord[1] /= nbNodes;
3813
3814   if ( !theSurface.IsNull() ) {
3815     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3816     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3817     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3818     coord[0] = p3d.X();
3819     coord[1] = p3d.Y();
3820     coord[2] = p3d.Z();
3821   }
3822   else
3823     coord[2] /= nbNodes;
3824
3825   // move node
3826
3827   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3828 }
3829
3830 //=======================================================================
3831 //function : centroidalSmooth
3832 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3833 //           surrounding elements
3834 //=======================================================================
3835
3836 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3837                       const Handle(Geom_Surface)&          theSurface,
3838                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3839 {
3840   gp_XYZ aNewXYZ(0.,0.,0.);
3841   SMESH::Controls::Area anAreaFunc;
3842   double totalArea = 0.;
3843   int nbElems = 0;
3844
3845   // compute new XYZ
3846
3847   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3848   while ( elemIt->more() )
3849   {
3850     const SMDS_MeshElement* elem = elemIt->next();
3851     nbElems++;
3852
3853     gp_XYZ elemCenter(0.,0.,0.);
3854     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3855     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3856     int nn = elem->NbNodes();
3857     if(elem->IsQuadratic()) nn = nn/2;
3858     int i=0;
3859     //while ( itN->more() ) {
3860     while ( i<nn ) {
3861       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3862       i++;
3863       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3864       aNodePoints.push_back( aP );
3865       if ( !theSurface.IsNull() ) { // smooth in 2D
3866         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3867         gp_XY* uv = theUVMap[ aNode ];
3868         aP.SetCoord( uv->X(), uv->Y(), 0. );
3869       }
3870       elemCenter += aP;
3871     }
3872     double elemArea = anAreaFunc.GetValue( aNodePoints );
3873     totalArea += elemArea;
3874     elemCenter /= nn;
3875     aNewXYZ += elemCenter * elemArea;
3876   }
3877   aNewXYZ /= totalArea;
3878   if ( !theSurface.IsNull() ) {
3879     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3880     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3881   }
3882
3883   // move node
3884
3885   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3886 }
3887
3888 //=======================================================================
3889 //function : getClosestUV
3890 //purpose  : return UV of closest projection
3891 //=======================================================================
3892
3893 static bool getClosestUV (Extrema_GenExtPS& projector,
3894                           const gp_Pnt&     point,
3895                           gp_XY &           result)
3896 {
3897   projector.Perform( point );
3898   if ( projector.IsDone() ) {
3899     double u, v, minVal = DBL_MAX;
3900     for ( int i = projector.NbExt(); i > 0; i-- )
3901       if ( projector.SquareDistance( i ) < minVal ) {
3902         minVal = projector.SquareDistance( i );
3903         projector.Point( i ).Parameter( u, v );
3904       }
3905     result.SetCoord( u, v );
3906     return true;
3907   }
3908   return false;
3909 }
3910
3911 //=======================================================================
3912 //function : Smooth
3913 //purpose  : Smooth theElements during theNbIterations or until a worst
3914 //           element has aspect ratio <= theTgtAspectRatio.
3915 //           Aspect Ratio varies in range [1.0, inf].
3916 //           If theElements is empty, the whole mesh is smoothed.
3917 //           theFixedNodes contains additionally fixed nodes. Nodes built
3918 //           on edges and boundary nodes are always fixed.
3919 //=======================================================================
3920
3921 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3922                                set<const SMDS_MeshNode*> & theFixedNodes,
3923                                const SmoothMethod          theSmoothMethod,
3924                                const int                   theNbIterations,
3925                                double                      theTgtAspectRatio,
3926                                const bool                  the2D)
3927 {
3928   myLastCreatedElems.Clear();
3929   myLastCreatedNodes.Clear();
3930
3931   if ( theTgtAspectRatio < 1.0 )
3932     theTgtAspectRatio = 1.0;
3933
3934   const double disttol = 1.e-16;
3935
3936   SMESH::Controls::AspectRatio aQualityFunc;
3937
3938   SMESHDS_Mesh* aMesh = GetMeshDS();
3939
3940   if ( theElems.empty() ) {
3941     // add all faces to theElems
3942     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3943     while ( fIt->more() ) {
3944       const SMDS_MeshElement* face = fIt->next();
3945       theElems.insert( theElems.end(), face );
3946     }
3947   }
3948   // get all face ids theElems are on
3949   set< int > faceIdSet;
3950   TIDSortedElemSet::iterator itElem;
3951   if ( the2D )
3952     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3953       int fId = FindShape( *itElem );
3954       // check that corresponding submesh exists and a shape is face
3955       if (fId &&
3956           faceIdSet.find( fId ) == faceIdSet.end() &&
3957           aMesh->MeshElements( fId )) {
3958         TopoDS_Shape F = aMesh->IndexToShape( fId );
3959         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3960           faceIdSet.insert( fId );
3961       }
3962     }
3963   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3964
3965   // ===============================================
3966   // smooth elements on each TopoDS_Face separately
3967   // ===============================================
3968
3969   SMESH_MesherHelper helper( *GetMesh() );
3970
3971   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3972   for ( ; fId != faceIdSet.rend(); ++fId )
3973   {
3974     // get face surface and submesh
3975     Handle(Geom_Surface) surface;
3976     SMESHDS_SubMesh* faceSubMesh = 0;
3977     TopoDS_Face face;
3978     double fToler2 = 0;
3979     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3980     bool isUPeriodic = false, isVPeriodic = false;
3981     if ( *fId )
3982     {
3983       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3984       surface = BRep_Tool::Surface( face );
3985       faceSubMesh = aMesh->MeshElements( *fId );
3986       fToler2 = BRep_Tool::Tolerance( face );
3987       fToler2 *= fToler2 * 10.;
3988       isUPeriodic = surface->IsUPeriodic();
3989       if ( isUPeriodic )
3990         surface->UPeriod();
3991       isVPeriodic = surface->IsVPeriodic();
3992       if ( isVPeriodic )
3993         surface->VPeriod();
3994       surface->Bounds( u1, u2, v1, v2 );
3995       helper.SetSubShape( face );
3996     }
3997     // ---------------------------------------------------------
3998     // for elements on a face, find movable and fixed nodes and
3999     // compute UV for them
4000     // ---------------------------------------------------------
4001     bool checkBoundaryNodes = false;
4002     bool isQuadratic = false;
4003     set<const SMDS_MeshNode*> setMovableNodes;
4004     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4005     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4006     list< const SMDS_MeshElement* > elemsOnFace;
4007
4008     Extrema_GenExtPS projector;
4009     GeomAdaptor_Surface surfAdaptor;
4010     if ( !surface.IsNull() ) {
4011       surfAdaptor.Load( surface );
4012       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4013     }
4014     int nbElemOnFace = 0;
4015     itElem = theElems.begin();
4016     // loop on not yet smoothed elements: look for elems on a face
4017     while ( itElem != theElems.end() )
4018     {
4019       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4020         break; // all elements found
4021
4022       const SMDS_MeshElement* elem = *itElem;
4023       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4024            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4025         ++itElem;
4026         continue;
4027       }
4028       elemsOnFace.push_back( elem );
4029       theElems.erase( itElem++ );
4030       nbElemOnFace++;
4031
4032       if ( !isQuadratic )
4033         isQuadratic = elem->IsQuadratic();
4034
4035       // get movable nodes of elem
4036       const SMDS_MeshNode* node;
4037       SMDS_TypeOfPosition posType;
4038       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4039       int nn = 0, nbn =  elem->NbNodes();
4040       if(elem->IsQuadratic())
4041         nbn = nbn/2;
4042       while ( nn++ < nbn ) {
4043         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4044         const SMDS_PositionPtr& pos = node->GetPosition();
4045         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4046         if (posType != SMDS_TOP_EDGE &&
4047             posType != SMDS_TOP_VERTEX &&
4048             theFixedNodes.find( node ) == theFixedNodes.end())
4049         {
4050           // check if all faces around the node are on faceSubMesh
4051           // because a node on edge may be bound to face
4052           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4053           bool all = true;
4054           if ( faceSubMesh ) {
4055             while ( eIt->more() && all ) {
4056               const SMDS_MeshElement* e = eIt->next();
4057               all = faceSubMesh->Contains( e );
4058             }
4059           }
4060           if ( all )
4061             setMovableNodes.insert( node );
4062           else
4063             checkBoundaryNodes = true;
4064         }
4065         if ( posType == SMDS_TOP_3DSPACE )
4066           checkBoundaryNodes = true;
4067       }
4068
4069       if ( surface.IsNull() )
4070         continue;
4071
4072       // get nodes to check UV
4073       list< const SMDS_MeshNode* > uvCheckNodes;
4074       const SMDS_MeshNode* nodeInFace = 0;
4075       itN = elem->nodesIterator();
4076       nn = 0; nbn =  elem->NbNodes();
4077       if(elem->IsQuadratic())
4078         nbn = nbn/2;
4079       while ( nn++ < nbn ) {
4080         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4081         if ( node->GetPosition()->GetDim() == 2 )
4082           nodeInFace = node;
4083         if ( uvMap.find( node ) == uvMap.end() )
4084           uvCheckNodes.push_back( node );
4085         // add nodes of elems sharing node
4086         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4087         //         while ( eIt->more() ) {
4088         //           const SMDS_MeshElement* e = eIt->next();
4089         //           if ( e != elem ) {
4090         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4091         //             while ( nIt->more() ) {
4092         //               const SMDS_MeshNode* n =
4093         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4094         //               if ( uvMap.find( n ) == uvMap.end() )
4095         //                 uvCheckNodes.push_back( n );
4096         //             }
4097         //           }
4098         //         }
4099       }
4100       // check UV on face
4101       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4102       for ( ; n != uvCheckNodes.end(); ++n ) {
4103         node = *n;
4104         gp_XY uv( 0, 0 );
4105         const SMDS_PositionPtr& pos = node->GetPosition();
4106         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4107         // get existing UV
4108         if ( pos )
4109         {
4110           bool toCheck = true;
4111           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4112         }
4113         // compute not existing UV
4114         bool project = ( posType == SMDS_TOP_3DSPACE );
4115         // double dist1 = DBL_MAX, dist2 = 0;
4116         // if ( posType != SMDS_TOP_3DSPACE ) {
4117         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4118         //   project = dist1 > fToler2;
4119         // }
4120         if ( project ) { // compute new UV
4121           gp_XY newUV;
4122           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4123           if ( !getClosestUV( projector, pNode, newUV )) {
4124             MESSAGE("Node Projection Failed " << node);
4125           }
4126           else {
4127             if ( isUPeriodic )
4128               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4129             if ( isVPeriodic )
4130               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4131             // check new UV
4132             // if ( posType != SMDS_TOP_3DSPACE )
4133             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4134             // if ( dist2 < dist1 )
4135               uv = newUV;
4136           }
4137         }
4138         // store UV in the map
4139         listUV.push_back( uv );
4140         uvMap.insert( make_pair( node, &listUV.back() ));
4141       }
4142     } // loop on not yet smoothed elements
4143
4144     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4145       checkBoundaryNodes = true;
4146
4147     // fix nodes on mesh boundary
4148
4149     if ( checkBoundaryNodes ) {
4150       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4151       map< SMESH_TLink, int >::iterator link_nb;
4152       // put all elements links to linkNbMap
4153       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4154       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4155         const SMDS_MeshElement* elem = (*elemIt);
4156         int nbn =  elem->NbCornerNodes();
4157         // loop on elem links: insert them in linkNbMap
4158         for ( int iN = 0; iN < nbn; ++iN ) {
4159           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4160           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4161           SMESH_TLink link( n1, n2 );
4162           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4163           link_nb->second++;
4164         }
4165       }
4166       // remove nodes that are in links encountered only once from setMovableNodes
4167       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4168         if ( link_nb->second == 1 ) {
4169           setMovableNodes.erase( link_nb->first.node1() );
4170           setMovableNodes.erase( link_nb->first.node2() );
4171         }
4172       }
4173     }
4174
4175     // -----------------------------------------------------
4176     // for nodes on seam edge, compute one more UV ( uvMap2 );
4177     // find movable nodes linked to nodes on seam and which
4178     // are to be smoothed using the second UV ( uvMap2 )
4179     // -----------------------------------------------------
4180
4181     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4182     if ( !surface.IsNull() ) {
4183       TopExp_Explorer eExp( face, TopAbs_EDGE );
4184       for ( ; eExp.More(); eExp.Next() ) {
4185         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4186         if ( !BRep_Tool::IsClosed( edge, face ))
4187           continue;
4188         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4189         if ( !sm ) continue;
4190         // find out which parameter varies for a node on seam
4191         double f,l;
4192         gp_Pnt2d uv1, uv2;
4193         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4194         if ( pcurve.IsNull() ) continue;
4195         uv1 = pcurve->Value( f );
4196         edge.Reverse();
4197         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4198         if ( pcurve.IsNull() ) continue;
4199         uv2 = pcurve->Value( f );
4200         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4201         // assure uv1 < uv2
4202         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4203           std::swap( uv1, uv2 );
4204         // get nodes on seam and its vertices
4205         list< const SMDS_MeshNode* > seamNodes;
4206         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4207         while ( nSeamIt->more() ) {
4208           const SMDS_MeshNode* node = nSeamIt->next();
4209           if ( !isQuadratic || !IsMedium( node ))
4210             seamNodes.push_back( node );
4211         }
4212         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4213         for ( ; vExp.More(); vExp.Next() ) {
4214           sm = aMesh->MeshElements( vExp.Current() );
4215           if ( sm ) {
4216             nSeamIt = sm->GetNodes();
4217             while ( nSeamIt->more() )
4218               seamNodes.push_back( nSeamIt->next() );
4219           }
4220         }
4221         // loop on nodes on seam
4222         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4223         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4224           const SMDS_MeshNode* nSeam = *noSeIt;
4225           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4226           if ( n_uv == uvMap.end() )
4227             continue;
4228           // set the first UV
4229           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4230           // set the second UV
4231           listUV.push_back( *n_uv->second );
4232           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4233           if ( uvMap2.empty() )
4234             uvMap2 = uvMap; // copy the uvMap contents
4235           uvMap2[ nSeam ] = &listUV.back();
4236
4237           // collect movable nodes linked to ones on seam in nodesNearSeam
4238           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4239           while ( eIt->more() ) {
4240             const SMDS_MeshElement* e = eIt->next();
4241             int nbUseMap1 = 0, nbUseMap2 = 0;
4242             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4243             int nn = 0, nbn =  e->NbNodes();
4244             if(e->IsQuadratic()) nbn = nbn/2;
4245             while ( nn++ < nbn )
4246             {
4247               const SMDS_MeshNode* n =
4248                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4249               if (n == nSeam ||
4250                   setMovableNodes.find( n ) == setMovableNodes.end() )
4251                 continue;
4252               // add only nodes being closer to uv2 than to uv1
4253               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4254               //              0.5 * ( n->Y() + nSeam->Y() ),
4255               //              0.5 * ( n->Z() + nSeam->Z() ));
4256               // gp_XY uv;
4257               // getClosestUV( projector, pMid, uv );
4258               double x = uvMap[ n ]->Coord( iPar );
4259               if ( Abs( uv1.Coord( iPar ) - x ) >
4260                    Abs( uv2.Coord( iPar ) - x )) {
4261                 nodesNearSeam.insert( n );
4262                 nbUseMap2++;
4263               }
4264               else
4265                 nbUseMap1++;
4266             }
4267             // for centroidalSmooth all element nodes must
4268             // be on one side of a seam
4269             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4270               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4271               nn = 0;
4272               while ( nn++ < nbn ) {
4273                 const SMDS_MeshNode* n =
4274                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4275                 setMovableNodes.erase( n );
4276               }
4277             }
4278           }
4279         } // loop on nodes on seam
4280       } // loop on edge of a face
4281     } // if ( !face.IsNull() )
4282
4283     if ( setMovableNodes.empty() ) {
4284       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4285       continue; // goto next face
4286     }
4287
4288     // -------------
4289     // SMOOTHING //
4290     // -------------
4291
4292     int it = -1;
4293     double maxRatio = -1., maxDisplacement = -1.;
4294     set<const SMDS_MeshNode*>::iterator nodeToMove;
4295     for ( it = 0; it < theNbIterations; it++ ) {
4296       maxDisplacement = 0.;
4297       nodeToMove = setMovableNodes.begin();
4298       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4299         const SMDS_MeshNode* node = (*nodeToMove);
4300         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4301
4302         // smooth
4303         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4304         if ( theSmoothMethod == LAPLACIAN )
4305           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4306         else
4307           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4308
4309         // node displacement
4310         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4311         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4312         if ( aDispl > maxDisplacement )
4313           maxDisplacement = aDispl;
4314       }
4315       // no node movement => exit
4316       //if ( maxDisplacement < 1.e-16 ) {
4317       if ( maxDisplacement < disttol ) {
4318         MESSAGE("-- no node movement --");
4319         break;
4320       }
4321
4322       // check elements quality
4323       maxRatio  = 0;
4324       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4325       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4326         const SMDS_MeshElement* elem = (*elemIt);
4327         if ( !elem || elem->GetType() != SMDSAbs_Face )
4328           continue;
4329         SMESH::Controls::TSequenceOfXYZ aPoints;
4330         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4331           double aValue = aQualityFunc.GetValue( aPoints );
4332           if ( aValue > maxRatio )
4333             maxRatio = aValue;
4334         }
4335       }
4336       if ( maxRatio <= theTgtAspectRatio ) {
4337         MESSAGE("-- quality achived --");
4338         break;
4339       }
4340       if (it+1 == theNbIterations) {
4341         MESSAGE("-- Iteration limit exceeded --");
4342       }
4343     } // smoothing iterations
4344
4345     MESSAGE(" Face id: " << *fId <<
4346             " Nb iterstions: " << it <<
4347             " Displacement: " << maxDisplacement <<
4348             " Aspect Ratio " << maxRatio);
4349
4350     // ---------------------------------------
4351     // new nodes positions are computed,
4352     // record movement in DS and set new UV
4353     // ---------------------------------------
4354     nodeToMove = setMovableNodes.begin();
4355     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4356       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4357       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4358       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4359       if ( node_uv != uvMap.end() ) {
4360         gp_XY* uv = node_uv->second;
4361         node->SetPosition
4362           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4363       }
4364     }
4365
4366     // move medium nodes of quadratic elements
4367     if ( isQuadratic )
4368     {
4369       vector<const SMDS_MeshNode*> nodes;
4370       bool checkUV;
4371       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4372       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4373       {
4374         const SMDS_MeshElement* QF = *elemIt;
4375         if ( QF->IsQuadratic() )
4376         {
4377           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4378                         SMDS_MeshElement::iterator() );
4379           nodes.push_back( nodes[0] );
4380           gp_Pnt xyz;
4381           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4382           {
4383             if ( !surface.IsNull() )
4384             {
4385               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4386               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4387               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4388               xyz = surface->Value( uv.X(), uv.Y() );
4389             }
4390             else {
4391               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4392             }
4393             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4394               // we have to move a medium node
4395               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4396           }
4397         }
4398       }
4399     }
4400
4401   } // loop on face ids
4402
4403 }
4404
4405 namespace
4406 {
4407   //=======================================================================
4408   //function : isReverse
4409   //purpose  : Return true if normal of prevNodes is not co-directied with
4410   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4411   //           iNotSame is where prevNodes and nextNodes are different.
4412   //           If result is true then future volume orientation is OK
4413   //=======================================================================
4414
4415   bool isReverse(const SMDS_MeshElement*             face,
4416                  const vector<const SMDS_MeshNode*>& prevNodes,
4417                  const vector<const SMDS_MeshNode*>& nextNodes,
4418                  const int                           iNotSame)
4419   {
4420
4421     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4422     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4423     gp_XYZ extrDir( pN - pP ), faceNorm;
4424     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4425
4426     return faceNorm * extrDir < 0.0;
4427   }
4428
4429   //================================================================================
4430   /*!
4431    * \brief Assure that theElemSets[0] holds elements, not nodes
4432    */
4433   //================================================================================
4434
4435   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4436   {
4437     if ( !theElemSets[0].empty() &&
4438          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4439     {
4440       std::swap( theElemSets[0], theElemSets[1] );
4441     }
4442     else if ( !theElemSets[1].empty() &&
4443               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4444     {
4445       std::swap( theElemSets[0], theElemSets[1] );
4446     }
4447   }
4448 }
4449
4450 //=======================================================================
4451 /*!
4452  * \brief Create elements by sweeping an element
4453  * \param elem - element to sweep
4454  * \param newNodesItVec - nodes generated from each node of the element
4455  * \param newElems - generated elements
4456  * \param nbSteps - number of sweeping steps
4457  * \param srcElements - to append elem for each generated element
4458  */
4459 //=======================================================================
4460
4461 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4462                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4463                                     list<const SMDS_MeshElement*>&        newElems,
4464                                     const size_t                          nbSteps,
4465                                     SMESH_SequenceOfElemPtr&              srcElements)
4466 {
4467   SMESHDS_Mesh* aMesh = GetMeshDS();
4468
4469   const int           nbNodes = elem->NbNodes();
4470   const int         nbCorners = elem->NbCornerNodes();
4471   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4472                                                           polyhedron creation !!! */
4473   // Loop on elem nodes:
4474   // find new nodes and detect same nodes indices
4475   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4476   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4477   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4478   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4479
4480   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4481   vector<int> sames(nbNodes);
4482   vector<bool> isSingleNode(nbNodes);
4483
4484   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4485     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4486     const SMDS_MeshNode*                         node = nnIt->first;
4487     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4488     if ( listNewNodes.empty() )
4489       return;
4490
4491     itNN   [ iNode ] = listNewNodes.begin();
4492     prevNod[ iNode ] = node;
4493     nextNod[ iNode ] = listNewNodes.front();
4494
4495     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4496                                                              corner node of linear */
4497     if ( prevNod[ iNode ] != nextNod [ iNode ])
4498       nbDouble += !isSingleNode[iNode];
4499
4500     if( iNode < nbCorners ) { // check corners only
4501       if ( prevNod[ iNode ] == nextNod [ iNode ])
4502         sames[nbSame++] = iNode;
4503       else
4504         iNotSameNode = iNode;
4505     }
4506   }
4507
4508   if ( nbSame == nbNodes || nbSame > 2) {
4509     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4510     return;
4511   }
4512
4513   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4514   {
4515     // fix nodes order to have bottom normal external
4516     if ( baseType == SMDSEntity_Polygon )
4517     {
4518       std::reverse( itNN.begin(), itNN.end() );
4519       std::reverse( prevNod.begin(), prevNod.end() );
4520       std::reverse( midlNod.begin(), midlNod.end() );
4521       std::reverse( nextNod.begin(), nextNod.end() );
4522       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4523     }
4524     else
4525     {
4526       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4527       SMDS_MeshCell::applyInterlace( ind, itNN );
4528       SMDS_MeshCell::applyInterlace( ind, prevNod );
4529       SMDS_MeshCell::applyInterlace( ind, nextNod );
4530       SMDS_MeshCell::applyInterlace( ind, midlNod );
4531       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4532       if ( nbSame > 0 )
4533       {
4534         sames[nbSame] = iNotSameNode;
4535         for ( int j = 0; j <= nbSame; ++j )
4536           for ( size_t i = 0; i < ind.size(); ++i )
4537             if ( ind[i] == sames[j] )
4538             {
4539               sames[j] = i;
4540               break;
4541             }
4542         iNotSameNode = sames[nbSame];
4543       }
4544     }
4545   }
4546   else if ( elem->GetType() == SMDSAbs_Edge )
4547   {
4548     // orient a new face same as adjacent one
4549     int i1, i2;
4550     const SMDS_MeshElement* e;
4551     TIDSortedElemSet dummy;
4552     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4553         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4554         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4555     {
4556       // there is an adjacent face, check order of nodes in it
4557       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4558       if ( sameOrder )
4559       {
4560         std::swap( itNN[0],    itNN[1] );
4561         std::swap( prevNod[0], prevNod[1] );
4562         std::swap( nextNod[0], nextNod[1] );
4563         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4564         if ( nbSame > 0 )
4565           sames[0] = 1 - sames[0];
4566         iNotSameNode = 1 - iNotSameNode;
4567       }
4568     }
4569   }
4570
4571   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4572   if ( nbSame > 0 ) {
4573     iSameNode    = sames[ nbSame-1 ];
4574     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4575     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4576     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4577   }
4578
4579   if ( baseType == SMDSEntity_Polygon )
4580   {
4581     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4582     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4583   }
4584   else if ( baseType == SMDSEntity_Quad_Polygon )
4585   {
4586     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4587     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4588   }
4589
4590   // make new elements
4591   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4592   {
4593     // get next nodes
4594     for ( iNode = 0; iNode < nbNodes; iNode++ )
4595     {
4596       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4597       nextNod[ iNode ] = *itNN[ iNode ]++;
4598     }
4599
4600     SMDS_MeshElement* aNewElem = 0;
4601     /*if(!elem->IsPoly())*/ {
4602       switch ( baseType ) {
4603       case SMDSEntity_0D:
4604       case SMDSEntity_Node: { // sweep NODE
4605         if ( nbSame == 0 ) {
4606           if ( isSingleNode[0] )
4607             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4608           else
4609             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4610         }
4611         else
4612           return;
4613         break;
4614       }
4615       case SMDSEntity_Edge: { // sweep EDGE
4616         if ( nbDouble == 0 )
4617         {
4618           if ( nbSame == 0 ) // ---> quadrangle
4619             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4620                                       nextNod[ 1 ], nextNod[ 0 ] );
4621           else               // ---> triangle
4622             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4623                                       nextNod[ iNotSameNode ] );
4624         }
4625         else                 // ---> polygon
4626         {
4627           vector<const SMDS_MeshNode*> poly_nodes;
4628           poly_nodes.push_back( prevNod[0] );
4629           poly_nodes.push_back( prevNod[1] );
4630           if ( prevNod[1] != nextNod[1] )
4631           {
4632             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4633             poly_nodes.push_back( nextNod[1] );
4634           }
4635           if ( prevNod[0] != nextNod[0] )
4636           {
4637             poly_nodes.push_back( nextNod[0] );
4638             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4639           }
4640           switch ( poly_nodes.size() ) {
4641           case 3:
4642             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4643             break;
4644           case 4:
4645             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4646                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4647             break;
4648           default:
4649             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4650           }
4651         }
4652         break;
4653       }
4654       case SMDSEntity_Triangle: // TRIANGLE --->
4655         {
4656           if ( nbDouble > 0 ) break;
4657           if ( nbSame == 0 )       // ---> pentahedron
4658             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4659                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4660
4661           else if ( nbSame == 1 )  // ---> pyramid
4662             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4663                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4664                                          nextNod[ iSameNode ]);
4665
4666           else // 2 same nodes:       ---> tetrahedron
4667             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4668                                          nextNod[ iNotSameNode ]);
4669           break;
4670         }
4671       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4672         {
4673           if ( nbSame == 2 )
4674             return;
4675           if ( nbDouble+nbSame == 2 )
4676           {
4677             if(nbSame==0) {      // ---> quadratic quadrangle
4678               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4679                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4680             }
4681             else { //(nbSame==1) // ---> quadratic triangle
4682               if(sames[0]==2) {
4683                 return; // medium node on axis
4684               }
4685               else if(sames[0]==0)
4686                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4687                                           prevNod[2], midlNod[1], nextNod[2] );
4688               else // sames[0]==1
4689                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4690                                           prevNod[2], nextNod[2], midlNod[0]);
4691             }
4692           }
4693           else if ( nbDouble == 3 )
4694           {
4695             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4696               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4697                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4698             }
4699           }
4700           else
4701             return;
4702           break;
4703         }
4704       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4705         if ( nbDouble > 0 ) break;
4706
4707         if ( nbSame == 0 )       // ---> hexahedron
4708           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4709                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4710
4711         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4712           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4713                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4714                                        nextNod[ iSameNode ]);
4715           newElems.push_back( aNewElem );
4716           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4717                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4718                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4719         }
4720         else if ( nbSame == 2 ) { // ---> pentahedron
4721           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4722             // iBeforeSame is same too
4723             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4724                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4725                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4726           else
4727             // iAfterSame is same too
4728             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4729                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4730                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4731         }
4732         break;
4733       }
4734       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4735       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4736         if ( nbDouble+nbSame != 3 ) break;
4737         if(nbSame==0) {
4738           // --->  pentahedron with 15 nodes
4739           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4740                                        nextNod[0], nextNod[1], nextNod[2],
4741                                        prevNod[3], prevNod[4], prevNod[5],
4742                                        nextNod[3], nextNod[4], nextNod[5],
4743                                        midlNod[0], midlNod[1], midlNod[2]);
4744         }
4745         else if(nbSame==1) {
4746           // --->  2d order pyramid of 13 nodes
4747           int apex = iSameNode;
4748           int i0 = ( apex + 1 ) % nbCorners;
4749           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4750           int i0a = apex + 3;
4751           int i1a = i1 + 3;
4752           int i01 = i0 + 3;
4753           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4754                                       nextNod[i0], nextNod[i1], prevNod[apex],
4755                                       prevNod[i01], midlNod[i0],
4756                                       nextNod[i01], midlNod[i1],
4757                                       prevNod[i1a], prevNod[i0a],
4758                                       nextNod[i0a], nextNod[i1a]);
4759         }
4760         else if(nbSame==2) {
4761           // --->  2d order tetrahedron of 10 nodes
4762           int n1 = iNotSameNode;
4763           int n2 = ( n1 + 1             ) % nbCorners;
4764           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4765           int n12 = n1 + 3;
4766           int n23 = n2 + 3;
4767           int n31 = n3 + 3;
4768           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4769                                        prevNod[n12], prevNod[n23], prevNod[n31],
4770                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4771         }
4772         break;
4773       }
4774       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4775         if( nbSame == 0 ) {
4776           if ( nbDouble != 4 ) break;
4777           // --->  hexahedron with 20 nodes
4778           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4779                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4780                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4781                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4782                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4783         }
4784         else if(nbSame==1) {
4785           // ---> pyramid + pentahedron - can not be created since it is needed
4786           // additional middle node at the center of face
4787           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4788           return;
4789         }
4790         else if( nbSame == 2 ) {
4791           if ( nbDouble != 2 ) break;
4792           // --->  2d order Pentahedron with 15 nodes
4793           int n1,n2,n4,n5;
4794           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4795             // iBeforeSame is same too
4796             n1 = iBeforeSame;
4797             n2 = iOpposSame;
4798             n4 = iSameNode;
4799             n5 = iAfterSame;
4800           }
4801           else {
4802             // iAfterSame is same too
4803             n1 = iSameNode;
4804             n2 = iBeforeSame;
4805             n4 = iAfterSame;
4806             n5 = iOpposSame;
4807           }
4808           int n12 = n2 + 4;
4809           int n45 = n4 + 4;
4810           int n14 = n1 + 4;
4811           int n25 = n5 + 4;
4812           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4813                                        prevNod[n4], prevNod[n5], nextNod[n5],
4814                                        prevNod[n12], midlNod[n2], nextNod[n12],
4815                                        prevNod[n45], midlNod[n5], nextNod[n45],
4816                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4817         }
4818         break;
4819       }
4820       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4821
4822         if( nbSame == 0 && nbDouble == 9 ) {
4823           // --->  tri-quadratic hexahedron with 27 nodes
4824           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4825                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4826                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4827                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4828                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4829                                        prevNod[8], // bottom center
4830                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4831                                        nextNod[8], // top center
4832                                        midlNod[8]);// elem center
4833         }
4834         else
4835         {
4836           return;
4837         }
4838         break;
4839       }
4840       case SMDSEntity_Polygon: { // sweep POLYGON
4841
4842         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4843           // --->  hexagonal prism
4844           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4845                                        prevNod[3], prevNod[4], prevNod[5],
4846                                        nextNod[0], nextNod[1], nextNod[2],
4847                                        nextNod[3], nextNod[4], nextNod[5]);
4848         }
4849         break;
4850       }
4851       case SMDSEntity_Ball:
4852         return;
4853
4854       default:
4855         break;
4856       } // switch ( baseType )
4857     } // scope
4858
4859     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4860     {
4861       if ( baseType != SMDSEntity_Polygon )
4862       {
4863         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4864         SMDS_MeshCell::applyInterlace( ind, prevNod );
4865         SMDS_MeshCell::applyInterlace( ind, nextNod );
4866         SMDS_MeshCell::applyInterlace( ind, midlNod );
4867         SMDS_MeshCell::applyInterlace( ind, itNN );
4868         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4869         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4870       }
4871       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4872       vector<int> quantities (nbNodes + 2);
4873       polyedre_nodes.clear();
4874       quantities.clear();
4875
4876       // bottom of prism
4877       for (int inode = 0; inode < nbNodes; inode++)
4878         polyedre_nodes.push_back( prevNod[inode] );
4879       quantities.push_back( nbNodes );
4880
4881       // top of prism
4882       polyedre_nodes.push_back( nextNod[0] );
4883       for (int inode = nbNodes; inode-1; --inode )
4884         polyedre_nodes.push_back( nextNod[inode-1] );
4885       quantities.push_back( nbNodes );
4886
4887       // side faces
4888       // 3--6--2
4889       // |     |
4890       // 7     5
4891       // |     |
4892       // 0--4--1
4893       const int iQuad = elem->IsQuadratic();
4894       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4895       {
4896         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4897         int inextface = (iface+1+iQuad) % nbNodes;
4898         int imid      = (iface+1) % nbNodes;
4899         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4900         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4901         polyedre_nodes.push_back( prevNod[iface] );             // 1
4902         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4903         {
4904           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4905           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4906         }
4907         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4908         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4909         {
4910           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4911           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4912         }
4913         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4914         if ( nbFaceNodes > 2 )
4915           quantities.push_back( nbFaceNodes );
4916         else // degenerated face
4917           polyedre_nodes.resize( prevNbNodes );
4918       }
4919       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4920
4921     } // try to create a polyherdal prism
4922
4923     if ( aNewElem ) {
4924       newElems.push_back( aNewElem );
4925       myLastCreatedElems.Append(aNewElem);
4926       srcElements.Append( elem );
4927     }
4928
4929     // set new prev nodes
4930     for ( iNode = 0; iNode < nbNodes; iNode++ )
4931       prevNod[ iNode ] = nextNod[ iNode ];
4932
4933   } // loop on steps
4934 }
4935
4936 //=======================================================================
4937 /*!
4938  * \brief Create 1D and 2D elements around swept elements
4939  * \param mapNewNodes - source nodes and ones generated from them
4940  * \param newElemsMap - source elements and ones generated from them
4941  * \param elemNewNodesMap - nodes generated from each node of each element
4942  * \param elemSet - all swept elements
4943  * \param nbSteps - number of sweeping steps
4944  * \param srcElements - to append elem for each generated element
4945  */
4946 //=======================================================================
4947
4948 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4949                                   TTElemOfElemListMap &    newElemsMap,
4950                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4951                                   TIDSortedElemSet&        elemSet,
4952                                   const int                nbSteps,
4953                                   SMESH_SequenceOfElemPtr& srcElements)
4954 {
4955   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4956   SMESHDS_Mesh* aMesh = GetMeshDS();
4957
4958   // Find nodes belonging to only one initial element - sweep them into edges.
4959
4960   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4961   for ( ; nList != mapNewNodes.end(); nList++ )
4962   {
4963     const SMDS_MeshNode* node =
4964       static_cast<const SMDS_MeshNode*>( nList->first );
4965     if ( newElemsMap.count( node ))
4966       continue; // node was extruded into edge
4967     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4968     int nbInitElems = 0;
4969     const SMDS_MeshElement* el = 0;
4970     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4971     while ( eIt->more() && nbInitElems < 2 ) {
4972       const SMDS_MeshElement* e = eIt->next();
4973       SMDSAbs_ElementType  type = e->GetType();
4974       if ( type == SMDSAbs_Volume ||
4975            type < highType ||
4976            !elemSet.count(e))
4977         continue;
4978       if ( type > highType ) {
4979         nbInitElems = 0;
4980         highType    = type;
4981       }
4982       el = e;
4983       ++nbInitElems;
4984     }
4985     if ( nbInitElems == 1 ) {
4986       bool NotCreateEdge = el && el->IsMediumNode(node);
4987       if(!NotCreateEdge) {
4988         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4989         list<const SMDS_MeshElement*> newEdges;
4990         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4991       }
4992     }
4993   }
4994
4995   // Make a ceiling for each element ie an equal element of last new nodes.
4996   // Find free links of faces - make edges and sweep them into faces.
4997
4998   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4999
5000   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5001   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5002   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5003   {
5004     const SMDS_MeshElement* elem = itElem->first;
5005     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5006
5007     if(itElem->second.size()==0) continue;
5008
5009     const bool isQuadratic = elem->IsQuadratic();
5010
5011     if ( elem->GetType() == SMDSAbs_Edge ) {
5012       // create a ceiling edge
5013       if ( !isQuadratic ) {
5014         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5015                                vecNewNodes[ 1 ]->second.back())) {
5016           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5017                                                    vecNewNodes[ 1 ]->second.back()));
5018           srcElements.Append( elem );
5019         }
5020       }
5021       else {
5022         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5023                                vecNewNodes[ 1 ]->second.back(),
5024                                vecNewNodes[ 2 ]->second.back())) {
5025           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5026                                                    vecNewNodes[ 1 ]->second.back(),
5027                                                    vecNewNodes[ 2 ]->second.back()));
5028           srcElements.Append( elem );
5029         }
5030       }
5031     }
5032     if ( elem->GetType() != SMDSAbs_Face )
5033       continue;
5034
5035     bool hasFreeLinks = false;
5036
5037     TIDSortedElemSet avoidSet;
5038     avoidSet.insert( elem );
5039
5040     set<const SMDS_MeshNode*> aFaceLastNodes;
5041     int iNode, nbNodes = vecNewNodes.size();
5042     if ( !isQuadratic ) {
5043       // loop on the face nodes
5044       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5045         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5046         // look for free links of the face
5047         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5048         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5049         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5050         // check if a link n1-n2 is free
5051         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5052           hasFreeLinks = true;
5053           // make a new edge and a ceiling for a new edge
5054           const SMDS_MeshElement* edge;
5055           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5056             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5057             srcElements.Append( myLastCreatedElems.Last() );
5058           }
5059           n1 = vecNewNodes[ iNode ]->second.back();
5060           n2 = vecNewNodes[ iNext ]->second.back();
5061           if ( !aMesh->FindEdge( n1, n2 )) {
5062             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5063             srcElements.Append( edge );
5064           }
5065         }
5066       }
5067     }
5068     else { // elem is quadratic face
5069       int nbn = nbNodes/2;
5070       for ( iNode = 0; iNode < nbn; iNode++ ) {
5071         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5072         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5073         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5074         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5075         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5076         // check if a link is free
5077         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5078              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5079              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5080           hasFreeLinks = true;
5081           // make an edge and a ceiling for a new edge
5082           // find medium node
5083           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5084             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5085             srcElements.Append( elem );
5086           }
5087           n1 = vecNewNodes[ iNode ]->second.back();
5088           n2 = vecNewNodes[ iNext ]->second.back();
5089           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5090           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5091             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5092             srcElements.Append( elem );
5093           }
5094         }
5095       }
5096       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5097         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5098       }
5099     }
5100
5101     // sweep free links into faces
5102
5103     if ( hasFreeLinks ) {
5104       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5105       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5106
5107       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5108       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5109       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5110         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5111         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5112       }
5113       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5114         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5115         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5116       }
5117       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5118         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5119         std::advance( v, volNb );
5120         // find indices of free faces of a volume and their source edges
5121         list< int > freeInd;
5122         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5123         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5124         int iF, nbF = vTool.NbFaces();
5125         for ( iF = 0; iF < nbF; iF ++ ) {
5126           if (vTool.IsFreeFace( iF ) &&
5127               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5128               initNodeSet != faceNodeSet) // except an initial face
5129           {
5130             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5131               continue;
5132             if ( faceNodeSet == initNodeSetNoCenter )
5133               continue;
5134             freeInd.push_back( iF );
5135             // find source edge of a free face iF
5136             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5137             vector<const SMDS_MeshNode*>::iterator lastCommom;
5138             commonNodes.resize( nbNodes, 0 );
5139             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5140                                                 initNodeSet.begin(), initNodeSet.end(),
5141                                                 commonNodes.begin());
5142             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5143               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5144             else
5145               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5146 #ifdef _DEBUG_
5147             if ( !srcEdges.back() )
5148             {
5149               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5150                    << iF << " of volume #" << vTool.ID() << endl;
5151             }
5152 #endif
5153           }
5154         }
5155         if ( freeInd.empty() )
5156           continue;
5157
5158         // create wall faces for all steps;
5159         // if such a face has been already created by sweep of edge,
5160         // assure that its orientation is OK
5161         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5162         {
5163           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5164           vTool.SetExternalNormal();
5165           const int nextShift = vTool.IsForward() ? +1 : -1;
5166           list< int >::iterator ind = freeInd.begin();
5167           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5168           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5169           {
5170             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5171             int nbn = vTool.NbFaceNodes( *ind );
5172             const SMDS_MeshElement * f = 0;
5173             if ( nbn == 3 )              ///// triangle
5174             {
5175               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5176               if ( !f ||
5177                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5178               {
5179                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5180                                                      nodes[ 1 ],
5181                                                      nodes[ 1 + nextShift ] };
5182                 if ( f )
5183                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5184                 else
5185                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5186                                                             newOrder[ 2 ] ));
5187               }
5188             }
5189             else if ( nbn == 4 )       ///// quadrangle
5190             {
5191               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5192               if ( !f ||
5193                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5194               {
5195                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5196                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5197                 if ( f )
5198                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5199                 else
5200                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5201                                                             newOrder[ 2 ], newOrder[ 3 ]));
5202               }
5203             }
5204             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5205             {
5206               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5207               if ( !f ||
5208                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5209               {
5210                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5211                                                      nodes[2],
5212                                                      nodes[2 + 2*nextShift],
5213                                                      nodes[3 - 2*nextShift],
5214                                                      nodes[3],
5215                                                      nodes[3 + 2*nextShift]};
5216                 if ( f )
5217                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5218                 else
5219                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5220                                                             newOrder[ 1 ],
5221                                                             newOrder[ 2 ],
5222                                                             newOrder[ 3 ],
5223                                                             newOrder[ 4 ],
5224                                                             newOrder[ 5 ] ));
5225               }
5226             }
5227             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5228             {
5229               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5230                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5231               if ( !f ||
5232                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5233               {
5234                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5235                                                      nodes[4 - 2*nextShift],
5236                                                      nodes[4],
5237                                                      nodes[4 + 2*nextShift],
5238                                                      nodes[1],
5239                                                      nodes[5 - 2*nextShift],
5240                                                      nodes[5],
5241                                                      nodes[5 + 2*nextShift] };
5242                 if ( f )
5243                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5244                 else
5245                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5246                                                            newOrder[ 2 ], newOrder[ 3 ],
5247                                                            newOrder[ 4 ], newOrder[ 5 ],
5248                                                            newOrder[ 6 ], newOrder[ 7 ]));
5249               }
5250             }
5251             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5252             {
5253               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5254                                       SMDSAbs_Face, /*noMedium=*/false);
5255               if ( !f ||
5256                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5257               {
5258                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5259                                                      nodes[4 - 2*nextShift],
5260                                                      nodes[4],
5261                                                      nodes[4 + 2*nextShift],
5262                                                      nodes[1],
5263                                                      nodes[5 - 2*nextShift],
5264                                                      nodes[5],
5265                                                      nodes[5 + 2*nextShift],
5266                                                      nodes[8] };
5267                 if ( f )
5268                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5269                 else
5270                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5271                                                            newOrder[ 2 ], newOrder[ 3 ],
5272                                                            newOrder[ 4 ], newOrder[ 5 ],
5273                                                            newOrder[ 6 ], newOrder[ 7 ],
5274                                                            newOrder[ 8 ]));
5275               }
5276             }
5277             else  //////// polygon
5278             {
5279               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5280               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5281               if ( !f ||
5282                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5283               {
5284                 if ( !vTool.IsForward() )
5285                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5286                 if ( f )
5287                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5288                 else
5289                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5290               }
5291             }
5292
5293             while ( srcElements.Length() < myLastCreatedElems.Length() )
5294               srcElements.Append( *srcEdge );
5295
5296           }  // loop on free faces
5297
5298           // go to the next volume
5299           iVol = 0;
5300           while ( iVol++ < nbVolumesByStep ) v++;
5301
5302         } // loop on steps
5303       } // loop on volumes of one step
5304     } // sweep free links into faces
5305
5306     // Make a ceiling face with a normal external to a volume
5307
5308     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5309     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5310     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5311
5312     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5313       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5314       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5315     }
5316     if ( iF >= 0 )
5317     {
5318       lastVol.SetExternalNormal();
5319       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5320       const               int nbn = lastVol.NbFaceNodes( iF );
5321       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5322       if ( !hasFreeLinks ||
5323            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5324       {
5325         const vector<int>& interlace =
5326           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5327         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5328
5329         AddElement( nodeVec, anyFace.Init( elem ));
5330
5331         while ( srcElements.Length() < myLastCreatedElems.Length() )
5332           srcElements.Append( elem );
5333       }
5334     }
5335   } // loop on swept elements
5336 }
5337
5338 //=======================================================================
5339 //function : RotationSweep
5340 //purpose  :
5341 //=======================================================================
5342
5343 SMESH_MeshEditor::PGroupIDs
5344 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5345                                 const gp_Ax1&      theAxis,
5346                                 const double       theAngle,
5347                                 const int          theNbSteps,
5348                                 const double       theTol,
5349                                 const bool         theMakeGroups,
5350                                 const bool         theMakeWalls)
5351 {
5352   myLastCreatedElems.Clear();
5353   myLastCreatedNodes.Clear();
5354
5355   // source elements for each generated one
5356   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5357
5358   gp_Trsf aTrsf;
5359   aTrsf.SetRotation( theAxis, theAngle );
5360   gp_Trsf aTrsf2;
5361   aTrsf2.SetRotation( theAxis, theAngle/2. );
5362
5363   gp_Lin aLine( theAxis );
5364   double aSqTol = theTol * theTol;
5365
5366   SMESHDS_Mesh* aMesh = GetMeshDS();
5367
5368   TNodeOfNodeListMap mapNewNodes;
5369   TElemOfVecOfNnlmiMap mapElemNewNodes;
5370   TTElemOfElemListMap newElemsMap;
5371
5372   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5373                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5374                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5375   // loop on theElemSets
5376   setElemsFirst( theElemSets );
5377   TIDSortedElemSet::iterator itElem;
5378   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5379   {
5380     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5381     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5382       const SMDS_MeshElement* elem = *itElem;
5383       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5384         continue;
5385       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5386       newNodesItVec.reserve( elem->NbNodes() );
5387
5388       // loop on elem nodes
5389       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5390       while ( itN->more() )
5391       {
5392         const SMDS_MeshNode* node = cast2Node( itN->next() );
5393
5394         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5395         double coord[3];
5396         aXYZ.Coord( coord[0], coord[1], coord[2] );
5397         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5398
5399         // check if a node has been already sweeped
5400         TNodeOfNodeListMapItr nIt =
5401           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5402         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5403         if ( listNewNodes.empty() )
5404         {
5405           // check if we are to create medium nodes between corner ones
5406           bool needMediumNodes = false;
5407           if ( isQuadraticMesh )
5408           {
5409             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5410             while (it->more() && !needMediumNodes )
5411             {
5412               const SMDS_MeshElement* invElem = it->next();
5413               if ( invElem != elem && !theElems.count( invElem )) continue;
5414               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5415               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5416                 needMediumNodes = true;
5417             }
5418           }
5419
5420           // make new nodes
5421           const SMDS_MeshNode * newNode = node;
5422           for ( int i = 0; i < theNbSteps; i++ ) {
5423             if ( !isOnAxis ) {
5424               if ( needMediumNodes )  // create a medium node
5425               {
5426                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5427                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5428                 myLastCreatedNodes.Append(newNode);
5429                 srcNodes.Append( node );
5430                 listNewNodes.push_back( newNode );
5431                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5432               }
5433               else {
5434                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5435               }
5436               // create a corner node
5437               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5438               myLastCreatedNodes.Append(newNode);
5439               srcNodes.Append( node );
5440               listNewNodes.push_back( newNode );
5441             }
5442             else {
5443               listNewNodes.push_back( newNode );
5444               // if ( needMediumNodes )
5445               //   listNewNodes.push_back( newNode );
5446             }
5447           }
5448         }
5449         newNodesItVec.push_back( nIt );
5450       }
5451       // make new elements
5452       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5453     }
5454   }
5455
5456   if ( theMakeWalls )
5457     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5458
5459   PGroupIDs newGroupIDs;
5460   if ( theMakeGroups )
5461     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5462
5463   return newGroupIDs;
5464 }
5465
5466 //=======================================================================
5467 //function : ExtrusParam
5468 //purpose  : standard construction
5469 //=======================================================================
5470
5471 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5472                                             const int      theNbSteps,
5473                                             const int      theFlags,
5474                                             const double   theTolerance):
5475   myDir( theStep ),
5476   myFlags( theFlags ),
5477   myTolerance( theTolerance ),
5478   myElemsToUse( NULL )
5479 {
5480   mySteps = new TColStd_HSequenceOfReal;
5481   const double stepSize = theStep.Magnitude();
5482   for (int i=1; i<=theNbSteps; i++ )
5483     mySteps->Append( stepSize );
5484
5485   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5486       ( theTolerance > 0 ))
5487   {
5488     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5489   }
5490   else
5491   {
5492     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5493   }
5494 }
5495
5496 //=======================================================================
5497 //function : ExtrusParam
5498 //purpose  : steps are given explicitly
5499 //=======================================================================
5500
5501 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5502                                             Handle(TColStd_HSequenceOfReal) theSteps,
5503                                             const int                       theFlags,
5504                                             const double                    theTolerance):
5505   myDir( theDir ),
5506   mySteps( theSteps ),
5507   myFlags( theFlags ),
5508   myTolerance( theTolerance ),
5509   myElemsToUse( NULL )
5510 {
5511   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5512       ( theTolerance > 0 ))
5513   {
5514     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5515   }
5516   else
5517   {
5518     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5519   }
5520 }
5521
5522 //=======================================================================
5523 //function : ExtrusParam
5524 //purpose  : for extrusion by normal
5525 //=======================================================================
5526
5527 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5528                                             const int    theNbSteps,
5529                                             const int    theFlags,
5530                                             const int    theDim ):
5531   myDir( 1,0,0 ),
5532   mySteps( new TColStd_HSequenceOfReal ),
5533   myFlags( theFlags ),
5534   myTolerance( 0 ),
5535   myElemsToUse( NULL )
5536 {
5537   for (int i = 0; i < theNbSteps; i++ )
5538     mySteps->Append( theStepSize );
5539
5540   if ( theDim == 1 )
5541   {
5542     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5543   }
5544   else
5545   {
5546     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5547   }
5548 }
5549
5550 //=======================================================================
5551 //function : ExtrusParam::SetElementsToUse
5552 //purpose  : stores elements to use for extrusion by normal, depending on
5553 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5554 //=======================================================================
5555
5556 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5557 {
5558   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5559 }
5560
5561 //=======================================================================
5562 //function : ExtrusParam::beginStepIter
5563 //purpose  : prepare iteration on steps
5564 //=======================================================================
5565
5566 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5567 {
5568   myWithMediumNodes = withMediumNodes;
5569   myNextStep = 1;
5570   myCurSteps.clear();
5571 }
5572 //=======================================================================
5573 //function : ExtrusParam::moreSteps
5574 //purpose  : are there more steps?
5575 //=======================================================================
5576
5577 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5578 {
5579   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5580 }
5581 //=======================================================================
5582 //function : ExtrusParam::nextStep
5583 //purpose  : returns the next step
5584 //=======================================================================
5585
5586 double SMESH_MeshEditor::ExtrusParam::nextStep()
5587 {
5588   double res = 0;
5589   if ( !myCurSteps.empty() )
5590   {
5591     res = myCurSteps.back();
5592     myCurSteps.pop_back();
5593   }
5594   else if ( myNextStep <= mySteps->Length() )
5595   {
5596     myCurSteps.push_back( mySteps->Value( myNextStep ));
5597     ++myNextStep;
5598     if ( myWithMediumNodes )
5599     {
5600       myCurSteps.back() /= 2.;
5601       myCurSteps.push_back( myCurSteps.back() );
5602     }
5603     res = nextStep();
5604   }
5605   return res;
5606 }
5607
5608 //=======================================================================
5609 //function : ExtrusParam::makeNodesByDir
5610 //purpose  : create nodes for standard extrusion
5611 //=======================================================================
5612
5613 int SMESH_MeshEditor::ExtrusParam::
5614 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5615                 const SMDS_MeshNode*              srcNode,
5616                 std::list<const SMDS_MeshNode*> & newNodes,
5617                 const bool                        makeMediumNodes)
5618 {
5619   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5620
5621   int nbNodes = 0;
5622   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5623   {
5624     p += myDir.XYZ() * nextStep();
5625     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5626     newNodes.push_back( newNode );
5627   }
5628   return nbNodes;
5629 }
5630
5631 //=======================================================================
5632 //function : ExtrusParam::makeNodesByDirAndSew
5633 //purpose  : create nodes for standard extrusion with sewing
5634 //=======================================================================
5635
5636 int SMESH_MeshEditor::ExtrusParam::
5637 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5638                       const SMDS_MeshNode*              srcNode,
5639                       std::list<const SMDS_MeshNode*> & newNodes,
5640                       const bool                        makeMediumNodes)
5641 {
5642   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5643
5644   int nbNodes = 0;
5645   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5646   {
5647     P1 += myDir.XYZ() * nextStep();
5648
5649     // try to search in sequence of existing nodes
5650     // if myNodes.Length()>0 we 'nave to use given sequence
5651     // else - use all nodes of mesh
5652     const SMDS_MeshNode * node = 0;
5653     if ( myNodes.Length() > 0 ) {
5654       int i;
5655       for(i=1; i<=myNodes.Length(); i++) {
5656         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5657         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5658         {
5659           node = myNodes.Value(i);
5660           break;
5661         }
5662       }
5663     }
5664     else {
5665       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5666       while(itn->more()) {
5667         SMESH_TNodeXYZ P2( itn->next() );
5668         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5669         {
5670           node = P2._node;
5671           break;
5672         }
5673       }
5674     }
5675
5676     if ( !node )
5677       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5678
5679     newNodes.push_back( node );
5680
5681   } // loop on steps
5682
5683   return nbNodes;
5684 }
5685
5686 //=======================================================================
5687 //function : ExtrusParam::makeNodesByNormal2D
5688 //purpose  : create nodes for extrusion using normals of faces
5689 //=======================================================================
5690
5691 int SMESH_MeshEditor::ExtrusParam::
5692 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5693                      const SMDS_MeshNode*              srcNode,
5694                      std::list<const SMDS_MeshNode*> & newNodes,
5695                      const bool                        makeMediumNodes)
5696 {
5697   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5698
5699   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5700
5701   // get normals to faces sharing srcNode
5702   vector< gp_XYZ > norms, baryCenters;
5703   gp_XYZ norm, avgNorm( 0,0,0 );
5704   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5705   while ( faceIt->more() )
5706   {
5707     const SMDS_MeshElement* face = faceIt->next();
5708     if ( myElemsToUse && !myElemsToUse->count( face ))
5709       continue;
5710     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5711     {
5712       norms.push_back( norm );
5713       avgNorm += norm;
5714       if ( !alongAvgNorm )
5715       {
5716         gp_XYZ bc(0,0,0);
5717         int nbN = 0;
5718         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5719           bc += SMESH_TNodeXYZ( nIt->next() );
5720         baryCenters.push_back( bc / nbN );
5721       }
5722     }
5723   }
5724
5725   if ( norms.empty() ) return 0;
5726
5727   double normSize = avgNorm.Modulus();
5728   if ( normSize < std::numeric_limits<double>::min() )
5729     return 0;
5730
5731   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5732   {
5733     myDir = avgNorm;
5734     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5735   }
5736
5737   avgNorm /= normSize;
5738
5739   int nbNodes = 0;
5740   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5741   {
5742     gp_XYZ pNew = p;
5743     double stepSize = nextStep();
5744
5745     if ( norms.size() > 1 )
5746     {
5747       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5748       {
5749         // translate plane of a face
5750         baryCenters[ iF ] += norms[ iF ] * stepSize;
5751
5752         // find point of intersection of the face plane located at baryCenters[ iF ]
5753         // and avgNorm located at pNew
5754         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5755         double dot  = ( norms[ iF ] * avgNorm );
5756         if ( dot < std::numeric_limits<double>::min() )
5757           dot = stepSize * 1e-3;
5758         double step = -( norms[ iF ] * pNew + d ) / dot;
5759         pNew += step * avgNorm;
5760       }
5761     }
5762     else
5763     {
5764       pNew += stepSize * avgNorm;
5765     }
5766     p = pNew;
5767
5768     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5769     newNodes.push_back( newNode );
5770   }
5771   return nbNodes;
5772 }
5773
5774 //=======================================================================
5775 //function : ExtrusParam::makeNodesByNormal1D
5776 //purpose  : create nodes for extrusion using normals of edges
5777 //=======================================================================
5778
5779 int SMESH_MeshEditor::ExtrusParam::
5780 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5781                      const SMDS_MeshNode*              srcNode,
5782                      std::list<const SMDS_MeshNode*> & newNodes,
5783                      const bool                        makeMediumNodes)
5784 {
5785   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5786   return 0;
5787 }
5788
5789 //=======================================================================
5790 //function : ExtrusionSweep
5791 //purpose  :
5792 //=======================================================================
5793
5794 SMESH_MeshEditor::PGroupIDs
5795 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5796                                   const gp_Vec&        theStep,
5797                                   const int            theNbSteps,
5798                                   TTElemOfElemListMap& newElemsMap,
5799                                   const int            theFlags,
5800                                   const double         theTolerance)
5801 {
5802   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5803   return ExtrusionSweep( theElems, aParams, newElemsMap );
5804 }
5805
5806
5807 //=======================================================================
5808 //function : ExtrusionSweep
5809 //purpose  :
5810 //=======================================================================
5811
5812 SMESH_MeshEditor::PGroupIDs
5813 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5814                                   ExtrusParam&         theParams,
5815                                   TTElemOfElemListMap& newElemsMap)
5816 {
5817   myLastCreatedElems.Clear();
5818   myLastCreatedNodes.Clear();
5819
5820   // source elements for each generated one
5821   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5822
5823   //SMESHDS_Mesh* aMesh = GetMeshDS();
5824
5825   setElemsFirst( theElemSets );
5826   const int nbSteps = theParams.NbSteps();
5827   theParams.SetElementsToUse( theElemSets[0] );
5828
5829   TNodeOfNodeListMap mapNewNodes;
5830   //TNodeOfNodeVecMap mapNewNodes;
5831   TElemOfVecOfNnlmiMap mapElemNewNodes;
5832   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5833
5834   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5835                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5836                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5837   // loop on theElems
5838   TIDSortedElemSet::iterator itElem;
5839   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5840   {
5841     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5842     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5843     {
5844       // check element type
5845       const SMDS_MeshElement* elem = *itElem;
5846       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5847         continue;
5848
5849       const size_t nbNodes = elem->NbNodes();
5850       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5851       newNodesItVec.reserve( nbNodes );
5852
5853       // loop on elem nodes
5854       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5855       while ( itN->more() )
5856       {
5857         // check if a node has been already sweeped
5858         const SMDS_MeshNode* node = cast2Node( itN->next() );
5859         TNodeOfNodeListMap::iterator nIt =
5860           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5861         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5862         if ( listNewNodes.empty() )
5863         {
5864           // make new nodes
5865
5866           // check if we are to create medium nodes between corner ones
5867           bool needMediumNodes = false;
5868           if ( isQuadraticMesh )
5869           {
5870             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5871             while (it->more() && !needMediumNodes )
5872             {
5873               const SMDS_MeshElement* invElem = it->next();
5874               if ( invElem != elem && !theElems.count( invElem )) continue;
5875               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5876               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5877                 needMediumNodes = true;
5878             }
5879           }
5880           // create nodes for all steps
5881           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5882           {
5883             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5884             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5885             {
5886               myLastCreatedNodes.Append( *newNodesIt );
5887               srcNodes.Append( node );
5888             }
5889           }
5890           else
5891           {
5892             break; // newNodesItVec will be shorter than nbNodes
5893           }
5894         }
5895         newNodesItVec.push_back( nIt );
5896       }
5897       // make new elements
5898       if ( newNodesItVec.size() == nbNodes )
5899         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5900     }
5901   }
5902
5903   if ( theParams.ToMakeBoundary() ) {
5904     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5905   }
5906   PGroupIDs newGroupIDs;
5907   if ( theParams.ToMakeGroups() )
5908     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5909
5910   return newGroupIDs;
5911 }
5912
5913 //=======================================================================
5914 //function : ExtrusionAlongTrack
5915 //purpose  :
5916 //=======================================================================
5917 SMESH_MeshEditor::Extrusion_Error
5918 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5919                                        SMESH_subMesh*       theTrack,
5920                                        const SMDS_MeshNode* theN1,
5921                                        const bool           theHasAngles,
5922                                        list<double>&        theAngles,
5923                                        const bool           theLinearVariation,
5924                                        const bool           theHasRefPoint,
5925                                        const gp_Pnt&        theRefPoint,
5926                                        const bool           theMakeGroups)
5927 {
5928   myLastCreatedElems.Clear();
5929   myLastCreatedNodes.Clear();
5930
5931   int aNbE;
5932   std::list<double> aPrms;
5933   TIDSortedElemSet::iterator itElem;
5934
5935   gp_XYZ aGC;
5936   TopoDS_Edge aTrackEdge;
5937   TopoDS_Vertex aV1, aV2;
5938
5939   SMDS_ElemIteratorPtr aItE;
5940   SMDS_NodeIteratorPtr aItN;
5941   SMDSAbs_ElementType aTypeE;
5942
5943   TNodeOfNodeListMap mapNewNodes;
5944
5945   // 1. Check data
5946   aNbE = theElements[0].size() + theElements[1].size();
5947   // nothing to do
5948   if ( !aNbE )
5949     return EXTR_NO_ELEMENTS;
5950
5951   // 1.1 Track Pattern
5952   ASSERT( theTrack );
5953
5954   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5955   if ( !pSubMeshDS )
5956     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5957                                 theHasAngles, theAngles, theLinearVariation,
5958                                 theHasRefPoint, theRefPoint, theMakeGroups );
5959
5960   aItE = pSubMeshDS->GetElements();
5961   while ( aItE->more() ) {
5962     const SMDS_MeshElement* pE = aItE->next();
5963     aTypeE = pE->GetType();
5964     // Pattern must contain links only
5965     if ( aTypeE != SMDSAbs_Edge )
5966       return EXTR_PATH_NOT_EDGE;
5967   }
5968
5969   list<SMESH_MeshEditor_PathPoint> fullList;
5970
5971   const TopoDS_Shape& aS = theTrack->GetSubShape();
5972   // Sub-shape for the Pattern must be an Edge or Wire
5973   if( aS.ShapeType() == TopAbs_EDGE ) {
5974     aTrackEdge = TopoDS::Edge( aS );
5975     // the Edge must not be degenerated
5976     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5977       return EXTR_BAD_PATH_SHAPE;
5978     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5979     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5980     const SMDS_MeshNode* aN1 = aItN->next();
5981     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5982     const SMDS_MeshNode* aN2 = aItN->next();
5983     // starting node must be aN1 or aN2
5984     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5985       return EXTR_BAD_STARTING_NODE;
5986     aItN = pSubMeshDS->GetNodes();
5987     while ( aItN->more() ) {
5988       const SMDS_MeshNode* pNode = aItN->next();
5989       const SMDS_EdgePosition* pEPos =
5990         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5991       double aT = pEPos->GetUParameter();
5992       aPrms.push_back( aT );
5993     }
5994     //Extrusion_Error err =
5995     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5996   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5997     list< SMESH_subMesh* > LSM;
5998     TopTools_SequenceOfShape Edges;
5999     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6000     while(itSM->more()) {
6001       SMESH_subMesh* SM = itSM->next();
6002       LSM.push_back(SM);
6003       const TopoDS_Shape& aS = SM->GetSubShape();
6004       Edges.Append(aS);
6005     }
6006     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6007     int startNid = theN1->GetID();
6008     TColStd_MapOfInteger UsedNums;
6009
6010     int NbEdges = Edges.Length();
6011     int i = 1;
6012     for(; i<=NbEdges; i++) {
6013       int k = 0;
6014       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6015       for(; itLSM!=LSM.end(); itLSM++) {
6016         k++;
6017         if(UsedNums.Contains(k)) continue;
6018         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6019         SMESH_subMesh* locTrack = *itLSM;
6020         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6021         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6022         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6023         const SMDS_MeshNode* aN1 = aItN->next();
6024         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6025         const SMDS_MeshNode* aN2 = aItN->next();
6026         // starting node must be aN1 or aN2
6027         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6028         // 2. Collect parameters on the track edge
6029         aPrms.clear();
6030         aItN = locMeshDS->GetNodes();
6031         while ( aItN->more() ) {
6032           const SMDS_MeshNode* pNode = aItN->next();
6033           const SMDS_EdgePosition* pEPos =
6034             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6035           double aT = pEPos->GetUParameter();
6036           aPrms.push_back( aT );
6037         }
6038         list<SMESH_MeshEditor_PathPoint> LPP;
6039         //Extrusion_Error err =
6040         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6041         LLPPs.push_back(LPP);
6042         UsedNums.Add(k);
6043         // update startN for search following egde
6044         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6045         else startNid = aN1->GetID();
6046         break;
6047       }
6048     }
6049     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6050     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6051     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6052     for(; itPP!=firstList.end(); itPP++) {
6053       fullList.push_back( *itPP );
6054     }
6055     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6056     fullList.pop_back();
6057     itLLPP++;
6058     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6059       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6060       itPP = currList.begin();
6061       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6062       gp_Dir D1 = PP1.Tangent();
6063       gp_Dir D2 = PP2.Tangent();
6064       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6065                            (D1.Z()+D2.Z())/2 ) );
6066       PP1.SetTangent(Dnew);
6067       fullList.push_back(PP1);
6068       itPP++;
6069       for(; itPP!=firstList.end(); itPP++) {
6070         fullList.push_back( *itPP );
6071       }
6072       PP1 = fullList.back();
6073       fullList.pop_back();
6074     }
6075     // if wire not closed
6076     fullList.push_back(PP1);
6077     // else ???
6078   }
6079   else {
6080     return EXTR_BAD_PATH_SHAPE;
6081   }
6082
6083   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6084                           theHasRefPoint, theRefPoint, theMakeGroups);
6085 }
6086
6087
6088 //=======================================================================
6089 //function : ExtrusionAlongTrack
6090 //purpose  :
6091 //=======================================================================
6092 SMESH_MeshEditor::Extrusion_Error
6093 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6094                                        SMESH_Mesh*          theTrack,
6095                                        const SMDS_MeshNode* theN1,
6096                                        const bool           theHasAngles,
6097                                        list<double>&        theAngles,
6098                                        const bool           theLinearVariation,
6099                                        const bool           theHasRefPoint,
6100                                        const gp_Pnt&        theRefPoint,
6101                                        const bool           theMakeGroups)
6102 {
6103   myLastCreatedElems.Clear();
6104   myLastCreatedNodes.Clear();
6105
6106   int aNbE;
6107   std::list<double> aPrms;
6108   TIDSortedElemSet::iterator itElem;
6109
6110   gp_XYZ aGC;
6111   TopoDS_Edge aTrackEdge;
6112   TopoDS_Vertex aV1, aV2;
6113
6114   SMDS_ElemIteratorPtr aItE;
6115   SMDS_NodeIteratorPtr aItN;
6116   SMDSAbs_ElementType aTypeE;
6117
6118   TNodeOfNodeListMap mapNewNodes;
6119
6120   // 1. Check data
6121   aNbE = theElements[0].size() + theElements[1].size();
6122   // nothing to do
6123   if ( !aNbE )
6124     return EXTR_NO_ELEMENTS;
6125
6126   // 1.1 Track Pattern
6127   ASSERT( theTrack );
6128
6129   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6130
6131   aItE = pMeshDS->elementsIterator();
6132   while ( aItE->more() ) {
6133     const SMDS_MeshElement* pE = aItE->next();
6134     aTypeE = pE->GetType();
6135     // Pattern must contain links only
6136     if ( aTypeE != SMDSAbs_Edge )
6137       return EXTR_PATH_NOT_EDGE;
6138   }
6139
6140   list<SMESH_MeshEditor_PathPoint> fullList;
6141
6142   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6143
6144   if ( !theTrack->HasShapeToMesh() ) {
6145     //Mesh without shape
6146     const SMDS_MeshNode* currentNode = NULL;
6147     const SMDS_MeshNode* prevNode = theN1;
6148     std::vector<const SMDS_MeshNode*> aNodesList;
6149     aNodesList.push_back(theN1);
6150     int nbEdges = 0, conn=0;
6151     const SMDS_MeshElement* prevElem = NULL;
6152     const SMDS_MeshElement* currentElem = NULL;
6153     int totalNbEdges = theTrack->NbEdges();
6154     SMDS_ElemIteratorPtr nIt;
6155
6156     //check start node
6157     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6158       return EXTR_BAD_STARTING_NODE;
6159     }
6160
6161     conn = nbEdgeConnectivity(theN1);
6162     if( conn != 1 )
6163       return EXTR_PATH_NOT_EDGE;
6164
6165     aItE = theN1->GetInverseElementIterator();
6166     prevElem = aItE->next();
6167     currentElem = prevElem;
6168     //Get all nodes
6169     if(totalNbEdges == 1 ) {
6170       nIt = currentElem->nodesIterator();
6171       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6172       if(currentNode == prevNode)
6173         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6174       aNodesList.push_back(currentNode);
6175     } else {
6176       nIt = currentElem->nodesIterator();
6177       while( nIt->more() ) {
6178         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6179         if(currentNode == prevNode)
6180           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6181         aNodesList.push_back(currentNode);
6182
6183         //case of the closed mesh
6184         if(currentNode == theN1) {
6185           nbEdges++;
6186           break;
6187         }
6188
6189         conn = nbEdgeConnectivity(currentNode);
6190         if(conn > 2) {
6191           return EXTR_PATH_NOT_EDGE;
6192         }else if( conn == 1 && nbEdges > 0 ) {
6193           //End of the path
6194           nbEdges++;
6195           break;
6196         }else {
6197           prevNode = currentNode;
6198           aItE = currentNode->GetInverseElementIterator();
6199           currentElem = aItE->next();
6200           if( currentElem  == prevElem)
6201             currentElem = aItE->next();
6202           nIt = currentElem->nodesIterator();
6203           prevElem = currentElem;
6204           nbEdges++;
6205         }
6206       }
6207     }
6208
6209     if(nbEdges != totalNbEdges)
6210       return EXTR_PATH_NOT_EDGE;
6211
6212     TopTools_SequenceOfShape Edges;
6213     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6214     int startNid = theN1->GetID();
6215     for ( size_t i = 1; i < aNodesList.size(); i++ )
6216     {
6217       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6218       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6219       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6220       list<SMESH_MeshEditor_PathPoint> LPP;
6221       aPrms.clear();
6222       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6223       LLPPs.push_back(LPP);
6224       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6225       else                                        startNid = aNodesList[i-1]->GetID();
6226     }
6227
6228     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6229     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6230     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6231     for(; itPP!=firstList.end(); itPP++) {
6232       fullList.push_back( *itPP );
6233     }
6234
6235     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6236     SMESH_MeshEditor_PathPoint PP2;
6237     fullList.pop_back();
6238     itLLPP++;
6239     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6240       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6241       itPP = currList.begin();
6242       PP2 = currList.front();
6243       gp_Dir D1 = PP1.Tangent();
6244       gp_Dir D2 = PP2.Tangent();
6245       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6246       PP1.SetTangent(Dnew);
6247       fullList.push_back(PP1);
6248       itPP++;
6249       for(; itPP!=currList.end(); itPP++) {
6250         fullList.push_back( *itPP );
6251       }
6252       PP1 = fullList.back();
6253       fullList.pop_back();
6254     }
6255     fullList.push_back(PP1);
6256
6257   } // Sub-shape for the Pattern must be an Edge or Wire
6258   else if ( aS.ShapeType() == TopAbs_EDGE )
6259   {
6260     aTrackEdge = TopoDS::Edge( aS );
6261     // the Edge must not be degenerated
6262     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6263       return EXTR_BAD_PATH_SHAPE;
6264     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6265     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6266     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6267     // starting node must be aN1 or aN2
6268     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6269       return EXTR_BAD_STARTING_NODE;
6270     aItN = pMeshDS->nodesIterator();
6271     while ( aItN->more() ) {
6272       const SMDS_MeshNode* pNode = aItN->next();
6273       if( pNode==aN1 || pNode==aN2 ) continue;
6274       const SMDS_EdgePosition* pEPos =
6275         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6276       double aT = pEPos->GetUParameter();
6277       aPrms.push_back( aT );
6278     }
6279     //Extrusion_Error err =
6280     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6281   }
6282   else if( aS.ShapeType() == TopAbs_WIRE ) {
6283     list< SMESH_subMesh* > LSM;
6284     TopTools_SequenceOfShape Edges;
6285     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6286     for(; eExp.More(); eExp.Next()) {
6287       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6288       if( SMESH_Algo::isDegenerated(E) ) continue;
6289       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6290       if(SM) {
6291         LSM.push_back(SM);
6292         Edges.Append(E);
6293       }
6294     }
6295     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6296     TopoDS_Vertex aVprev;
6297     TColStd_MapOfInteger UsedNums;
6298     int NbEdges = Edges.Length();
6299     int i = 1;
6300     for(; i<=NbEdges; i++) {
6301       int k = 0;
6302       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6303       for(; itLSM!=LSM.end(); itLSM++) {
6304         k++;
6305         if(UsedNums.Contains(k)) continue;
6306         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6307         SMESH_subMesh* locTrack = *itLSM;
6308         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6309         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6310         bool aN1isOK = false, aN2isOK = false;
6311         if ( aVprev.IsNull() ) {
6312           // if previous vertex is not yet defined, it means that we in the beginning of wire
6313           // and we have to find initial vertex corresponding to starting node theN1
6314           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6315           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6316           // starting node must be aN1 or aN2
6317           aN1isOK = ( aN1 && aN1 == theN1 );
6318           aN2isOK = ( aN2 && aN2 == theN1 );
6319         }
6320         else {
6321           // we have specified ending vertex of the previous edge on the previous iteration
6322           // and we have just to check that it corresponds to any vertex in current segment
6323           aN1isOK = aVprev.IsSame( aV1 );
6324           aN2isOK = aVprev.IsSame( aV2 );
6325         }
6326         if ( !aN1isOK && !aN2isOK ) continue;
6327         // 2. Collect parameters on the track edge
6328         aPrms.clear();
6329         aItN = locMeshDS->GetNodes();
6330         while ( aItN->more() ) {
6331           const SMDS_MeshNode*     pNode = aItN->next();
6332           const SMDS_EdgePosition* pEPos =
6333             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6334           double aT = pEPos->GetUParameter();
6335           aPrms.push_back( aT );
6336         }
6337         list<SMESH_MeshEditor_PathPoint> LPP;
6338         //Extrusion_Error err =
6339         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6340         LLPPs.push_back(LPP);
6341         UsedNums.Add(k);
6342         // update startN for search following egde
6343         if ( aN1isOK ) aVprev = aV2;
6344         else           aVprev = aV1;
6345         break;
6346       }
6347     }
6348     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6349     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6350     fullList.splice( fullList.end(), firstList );
6351
6352     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6353     fullList.pop_back();
6354     itLLPP++;
6355     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6356       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6357       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6358       gp_Dir D1 = PP1.Tangent();
6359       gp_Dir D2 = PP2.Tangent();
6360       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6361       PP1.SetTangent(Dnew);
6362       fullList.push_back(PP1);
6363       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6364       PP1 = fullList.back();
6365       fullList.pop_back();
6366     }
6367     // if wire not closed
6368     fullList.push_back(PP1);
6369     // else ???
6370   }
6371   else {
6372     return EXTR_BAD_PATH_SHAPE;
6373   }
6374
6375   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6376                           theHasRefPoint, theRefPoint, theMakeGroups);
6377 }
6378
6379
6380 //=======================================================================
6381 //function : MakeEdgePathPoints
6382 //purpose  : auxilary for ExtrusionAlongTrack
6383 //=======================================================================
6384 SMESH_MeshEditor::Extrusion_Error
6385 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6386                                      const TopoDS_Edge&                aTrackEdge,
6387                                      bool                              FirstIsStart,
6388                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6389 {
6390   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6391   aTolVec=1.e-7;
6392   aTolVec2=aTolVec*aTolVec;
6393   double aT1, aT2;
6394   TopoDS_Vertex aV1, aV2;
6395   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6396   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6397   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6398   // 2. Collect parameters on the track edge
6399   aPrms.push_front( aT1 );
6400   aPrms.push_back( aT2 );
6401   // sort parameters
6402   aPrms.sort();
6403   if( FirstIsStart ) {
6404     if ( aT1 > aT2 ) {
6405       aPrms.reverse();
6406     }
6407   }
6408   else {
6409     if ( aT2 > aT1 ) {
6410       aPrms.reverse();
6411     }
6412   }
6413   // 3. Path Points
6414   SMESH_MeshEditor_PathPoint aPP;
6415   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6416   std::list<double>::iterator aItD = aPrms.begin();
6417   for(; aItD != aPrms.end(); ++aItD) {
6418     double aT = *aItD;
6419     gp_Pnt aP3D;
6420     gp_Vec aVec;
6421     aC3D->D1( aT, aP3D, aVec );
6422     aL2 = aVec.SquareMagnitude();
6423     if ( aL2 < aTolVec2 )
6424       return EXTR_CANT_GET_TANGENT;
6425     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6426     aPP.SetPnt( aP3D );
6427     aPP.SetTangent( aTgt );
6428     aPP.SetParameter( aT );
6429     LPP.push_back(aPP);
6430   }
6431   return EXTR_OK;
6432 }
6433
6434
6435 //=======================================================================
6436 //function : MakeExtrElements
6437 //purpose  : auxilary for ExtrusionAlongTrack
6438 //=======================================================================
6439 SMESH_MeshEditor::Extrusion_Error
6440 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6441                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6442                                    const bool                        theHasAngles,
6443                                    list<double>&                     theAngles,
6444                                    const bool                        theLinearVariation,
6445                                    const bool                        theHasRefPoint,
6446                                    const gp_Pnt&                     theRefPoint,
6447                                    const bool                        theMakeGroups)
6448 {
6449   const int aNbTP = fullList.size();
6450
6451   // Angles
6452   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6453     LinearAngleVariation(aNbTP-1, theAngles);
6454
6455   // fill vector of path points with angles
6456   vector<SMESH_MeshEditor_PathPoint> aPPs;
6457   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6458   list<double>::iterator                 itAngles = theAngles.begin();
6459   aPPs.push_back( *itPP++ );
6460   for( ; itPP != fullList.end(); itPP++) {
6461     aPPs.push_back( *itPP );
6462     if ( theHasAngles && itAngles != theAngles.end() )
6463       aPPs.back().SetAngle( *itAngles++ );
6464   }
6465
6466   TNodeOfNodeListMap   mapNewNodes;
6467   TElemOfVecOfNnlmiMap mapElemNewNodes;
6468   TTElemOfElemListMap  newElemsMap;
6469   TIDSortedElemSet::iterator itElem;
6470   // source elements for each generated one
6471   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6472
6473   // 3. Center of rotation aV0
6474   gp_Pnt aV0 = theRefPoint;
6475   if ( !theHasRefPoint )
6476   {
6477     gp_XYZ aGC( 0.,0.,0. );
6478     TIDSortedElemSet newNodes;
6479
6480     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6481     {
6482       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6483       itElem = theElements.begin();
6484       for ( ; itElem != theElements.end(); itElem++ )
6485       {
6486         const SMDS_MeshElement* elem = *itElem;
6487         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6488         while ( itN->more() ) {
6489           const SMDS_MeshElement* node = itN->next();
6490           if ( newNodes.insert( node ).second )
6491             aGC += SMESH_TNodeXYZ( node );
6492         }
6493       }
6494     }
6495     aGC /= newNodes.size();
6496     aV0.SetXYZ( aGC );
6497   } // if (!theHasRefPoint) {
6498
6499   // 4. Processing the elements
6500   SMESHDS_Mesh* aMesh = GetMeshDS();
6501   list<const SMDS_MeshNode*> emptyList;
6502
6503   setElemsFirst( theElemSets );
6504   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6505   {
6506     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6507     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6508     {
6509       const SMDS_MeshElement* elem = *itElem;
6510
6511       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6512       newNodesItVec.reserve( elem->NbNodes() );
6513
6514       // loop on elem nodes
6515       int nodeIndex = -1;
6516       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6517       while ( itN->more() )
6518       {
6519         ++nodeIndex;
6520         // check if a node has been already processed
6521         const SMDS_MeshNode* node = cast2Node( itN->next() );
6522         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6523         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6524         if ( listNewNodes.empty() )
6525         {
6526           // make new nodes
6527           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6528           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6529           gp_Ax1 anAx1, anAxT1T0;
6530           gp_Dir aDT1x, aDT0x, aDT1T0;
6531
6532           aTolAng=1.e-4;
6533
6534           aV0x = aV0;
6535           aPN0 = SMESH_TNodeXYZ( node );
6536
6537           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6538           aP0x = aPP0.Pnt();
6539           aDT0x= aPP0.Tangent();
6540
6541           for ( int j = 1; j < aNbTP; ++j ) {
6542             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6543             aP1x     = aPP1.Pnt();
6544             aDT1x    = aPP1.Tangent();
6545             aAngle1x = aPP1.Angle();
6546
6547             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6548             // Translation
6549             gp_Vec aV01x( aP0x, aP1x );
6550             aTrsf.SetTranslation( aV01x );
6551
6552             // traslated point
6553             aV1x = aV0x.Transformed( aTrsf );
6554             aPN1 = aPN0.Transformed( aTrsf );
6555
6556             // rotation 1 [ T1,T0 ]
6557             aAngleT1T0=-aDT1x.Angle( aDT0x );
6558             if (fabs(aAngleT1T0) > aTolAng)
6559             {
6560               aDT1T0=aDT1x^aDT0x;
6561               anAxT1T0.SetLocation( aV1x );
6562               anAxT1T0.SetDirection( aDT1T0 );
6563               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6564
6565               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6566             }
6567
6568             // rotation 2
6569             if ( theHasAngles ) {
6570               anAx1.SetLocation( aV1x );
6571               anAx1.SetDirection( aDT1x );
6572               aTrsfRot.SetRotation( anAx1, aAngle1x );
6573
6574               aPN1 = aPN1.Transformed( aTrsfRot );
6575             }
6576
6577             // make new node
6578             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6579             {
6580               // create additional node
6581               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6582               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6583               myLastCreatedNodes.Append(newNode);
6584               srcNodes.Append( node );
6585               listNewNodes.push_back( newNode );
6586             }
6587             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6588             myLastCreatedNodes.Append(newNode);
6589             srcNodes.Append( node );
6590             listNewNodes.push_back( newNode );
6591
6592             aPN0 = aPN1;
6593             aP0x = aP1x;
6594             aV0x = aV1x;
6595             aDT0x = aDT1x;
6596           }
6597         }
6598         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6599         {
6600           // if current elem is quadratic and current node is not medium
6601           // we have to check - may be it is needed to insert additional nodes
6602           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6603           if ((int) listNewNodes.size() == aNbTP-1 )
6604           {
6605             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6606             gp_XYZ P(node->X(), node->Y(), node->Z());
6607             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6608             int i;
6609             for(i=0; i<aNbTP-1; i++) {
6610               const SMDS_MeshNode* N = *it;
6611               double x = ( N->X() + P.X() )/2.;
6612               double y = ( N->Y() + P.Y() )/2.;
6613               double z = ( N->Z() + P.Z() )/2.;
6614               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6615               srcNodes.Append( node );
6616               myLastCreatedNodes.Append(newN);
6617               aNodes[2*i] = newN;
6618               aNodes[2*i+1] = N;
6619               P = gp_XYZ(N->X(),N->Y(),N->Z());
6620             }
6621             listNewNodes.clear();
6622             for(i=0; i<2*(aNbTP-1); i++) {
6623               listNewNodes.push_back(aNodes[i]);
6624             }
6625           }
6626         }
6627
6628         newNodesItVec.push_back( nIt );
6629       }
6630
6631       // make new elements
6632       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6633     }
6634   }
6635
6636   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6637
6638   if ( theMakeGroups )
6639     generateGroups( srcNodes, srcElems, "extruded");
6640
6641   return EXTR_OK;
6642 }
6643
6644
6645 //=======================================================================
6646 //function : LinearAngleVariation
6647 //purpose  : auxilary for ExtrusionAlongTrack
6648 //=======================================================================
6649 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6650                                             list<double>& Angles)
6651 {
6652   int nbAngles = Angles.size();
6653   if( nbSteps > nbAngles ) {
6654     vector<double> theAngles(nbAngles);
6655     list<double>::iterator it = Angles.begin();
6656     int i = -1;
6657     for(; it!=Angles.end(); it++) {
6658       i++;
6659       theAngles[i] = (*it);
6660     }
6661     list<double> res;
6662     double rAn2St = double( nbAngles ) / double( nbSteps );
6663     double angPrev = 0, angle;
6664     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6665       double angCur = rAn2St * ( iSt+1 );
6666       double angCurFloor  = floor( angCur );
6667       double angPrevFloor = floor( angPrev );
6668       if ( angPrevFloor == angCurFloor )
6669         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6670       else {
6671         int iP = int( angPrevFloor );
6672         double angPrevCeil = ceil(angPrev);
6673         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6674
6675         int iC = int( angCurFloor );
6676         if ( iC < nbAngles )
6677           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6678
6679         iP = int( angPrevCeil );
6680         while ( iC-- > iP )
6681           angle += theAngles[ iC ];
6682       }
6683       res.push_back(angle);
6684       angPrev = angCur;
6685     }
6686     Angles.clear();
6687     it = res.begin();
6688     for(; it!=res.end(); it++)
6689       Angles.push_back( *it );
6690   }
6691 }
6692
6693
6694 //================================================================================
6695 /*!
6696  * \brief Move or copy theElements applying theTrsf to their nodes
6697  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6698  *  \param theTrsf - transformation to apply
6699  *  \param theCopy - if true, create translated copies of theElems
6700  *  \param theMakeGroups - if true and theCopy, create translated groups
6701  *  \param theTargetMesh - mesh to copy translated elements into
6702  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6703  */
6704 //================================================================================
6705
6706 SMESH_MeshEditor::PGroupIDs
6707 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6708                              const gp_Trsf&     theTrsf,
6709                              const bool         theCopy,
6710                              const bool         theMakeGroups,
6711                              SMESH_Mesh*        theTargetMesh)
6712 {
6713   myLastCreatedElems.Clear();
6714   myLastCreatedNodes.Clear();
6715
6716   bool needReverse = false;
6717   string groupPostfix;
6718   switch ( theTrsf.Form() ) {
6719   case gp_PntMirror:
6720     needReverse = true;
6721     groupPostfix = "mirrored";
6722     break;
6723   case gp_Ax1Mirror:
6724     groupPostfix = "mirrored";
6725     break;
6726   case gp_Ax2Mirror:
6727     needReverse = true;
6728     groupPostfix = "mirrored";
6729     break;
6730   case gp_Rotation:
6731     groupPostfix = "rotated";
6732     break;
6733   case gp_Translation:
6734     groupPostfix = "translated";
6735     break;
6736   case gp_Scale:
6737     groupPostfix = "scaled";
6738     break;
6739   case gp_CompoundTrsf: // different scale by axis
6740     groupPostfix = "scaled";
6741     break;
6742   default:
6743     needReverse = false;
6744     groupPostfix = "transformed";
6745   }
6746
6747   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6748   SMESHDS_Mesh* aMesh    = GetMeshDS();
6749
6750   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6751   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6752   SMESH_MeshEditor::ElemFeatures elemType;
6753
6754   // map old node to new one
6755   TNodeNodeMap nodeMap;
6756
6757   // elements sharing moved nodes; those of them which have all
6758   // nodes mirrored but are not in theElems are to be reversed
6759   TIDSortedElemSet inverseElemSet;
6760
6761   // source elements for each generated one
6762   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6763
6764   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6765   TIDSortedElemSet orphanNode;
6766
6767   if ( theElems.empty() ) // transform the whole mesh
6768   {
6769     // add all elements
6770     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6771     while ( eIt->more() ) theElems.insert( eIt->next() );
6772     // add orphan nodes
6773     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6774     while ( nIt->more() )
6775     {
6776       const SMDS_MeshNode* node = nIt->next();
6777       if ( node->NbInverseElements() == 0)
6778         orphanNode.insert( node );
6779     }
6780   }
6781
6782   // loop on elements to transform nodes : first orphan nodes then elems
6783   TIDSortedElemSet::iterator itElem;
6784   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6785   for (int i=0; i<2; i++)
6786     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6787     {
6788       const SMDS_MeshElement* elem = *itElem;
6789       if ( !elem )
6790         continue;
6791
6792       // loop on elem nodes
6793       double coord[3];
6794       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6795       while ( itN->more() )
6796       {
6797         const SMDS_MeshNode* node = cast2Node( itN->next() );
6798         // check if a node has been already transformed
6799         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6800           nodeMap.insert( make_pair ( node, node ));
6801         if ( !n2n_isnew.second )
6802           continue;
6803
6804         node->GetXYZ( coord );
6805         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6806         if ( theTargetMesh ) {
6807           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6808           n2n_isnew.first->second = newNode;
6809           myLastCreatedNodes.Append(newNode);
6810           srcNodes.Append( node );
6811         }
6812         else if ( theCopy ) {
6813           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6814           n2n_isnew.first->second = newNode;
6815           myLastCreatedNodes.Append(newNode);
6816           srcNodes.Append( node );
6817         }
6818         else {
6819           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6820           // node position on shape becomes invalid
6821           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6822             ( SMDS_SpacePosition::originSpacePosition() );
6823         }
6824
6825         // keep inverse elements
6826         if ( !theCopy && !theTargetMesh && needReverse ) {
6827           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6828           while ( invElemIt->more() ) {
6829             const SMDS_MeshElement* iel = invElemIt->next();
6830             inverseElemSet.insert( iel );
6831           }
6832         }
6833       }
6834     } // loop on elems in { &orphanNode, &theElems };
6835
6836   // either create new elements or reverse mirrored ones
6837   if ( !theCopy && !needReverse && !theTargetMesh )
6838     return PGroupIDs();
6839
6840   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6841
6842   // Replicate or reverse elements
6843
6844   std::vector<int> iForw;
6845   vector<const SMDS_MeshNode*> nodes;
6846   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6847   {
6848     const SMDS_MeshElement* elem = *itElem;
6849     if ( !elem ) continue;
6850
6851     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6852     size_t               nbNodes  = elem->NbNodes();
6853     if ( geomType == SMDSGeom_NONE ) continue; // node
6854
6855     nodes.resize( nbNodes );
6856
6857     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6858     {
6859       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6860       if (!aPolyedre)
6861         continue;
6862       nodes.clear();
6863       bool allTransformed = true;
6864       int nbFaces = aPolyedre->NbFaces();
6865       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6866       {
6867         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6868         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6869         {
6870           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6871           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6872           if ( nodeMapIt == nodeMap.end() )
6873             allTransformed = false; // not all nodes transformed
6874           else
6875             nodes.push_back((*nodeMapIt).second);
6876         }
6877         if ( needReverse && allTransformed )
6878           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6879       }
6880       if ( !allTransformed )
6881         continue; // not all nodes transformed
6882     }
6883     else // ----------------------- the rest element types
6884     {
6885       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6886       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6887       const vector<int>&    i = needReverse ? iRev : iForw;
6888
6889       // find transformed nodes
6890       size_t iNode = 0;
6891       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6892       while ( itN->more() ) {
6893         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6894         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6895         if ( nodeMapIt == nodeMap.end() )
6896           break; // not all nodes transformed
6897         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6898       }
6899       if ( iNode != nbNodes )
6900         continue; // not all nodes transformed
6901     }
6902
6903     if ( editor ) {
6904       // copy in this or a new mesh
6905       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6906         srcElems.Append( elem );
6907     }
6908     else {
6909       // reverse element as it was reversed by transformation
6910       if ( nbNodes > 2 )
6911         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6912     }
6913
6914   } // loop on elements
6915
6916   if ( editor && editor != this )
6917     myLastCreatedElems = editor->myLastCreatedElems;
6918
6919   PGroupIDs newGroupIDs;
6920
6921   if ( ( theMakeGroups && theCopy ) ||
6922        ( theMakeGroups && theTargetMesh ) )
6923     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6924
6925   return newGroupIDs;
6926 }
6927
6928 //=======================================================================
6929 /*!
6930  * \brief Create groups of elements made during transformation
6931  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6932  *  \param elemGens - elements making corresponding myLastCreatedElems
6933  *  \param postfix - to append to names of new groups
6934  *  \param targetMesh - mesh to create groups in
6935  *  \param topPresent - is there "top" elements that are created by sweeping
6936  */
6937 //=======================================================================
6938
6939 SMESH_MeshEditor::PGroupIDs
6940 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6941                                  const SMESH_SequenceOfElemPtr& elemGens,
6942                                  const std::string&             postfix,
6943                                  SMESH_Mesh*                    targetMesh,
6944                                  const bool                     topPresent)
6945 {
6946   PGroupIDs newGroupIDs( new list<int> );
6947   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6948
6949   // Sort existing groups by types and collect their names
6950
6951   // containers to store an old group and generated new ones;
6952   // 1st new group is for result elems of different type than a source one;
6953   // 2nd new group is for same type result elems ("top" group at extrusion)
6954   using boost::tuple;
6955   using boost::make_tuple;
6956   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6957   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6958   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6959   // group names
6960   set< string > groupNames;
6961
6962   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6963   if ( !groupIt->more() ) return newGroupIDs;
6964
6965   int newGroupID = mesh->GetGroupIds().back()+1;
6966   while ( groupIt->more() )
6967   {
6968     SMESH_Group * group = groupIt->next();
6969     if ( !group ) continue;
6970     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6971     if ( !groupDS || groupDS->IsEmpty() ) continue;
6972     groupNames.insert    ( group->GetName() );
6973     groupDS->SetStoreName( group->GetName() );
6974     const SMDSAbs_ElementType type = groupDS->GetType();
6975     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6976     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6977     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6978     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6979   }
6980
6981   // Loop on nodes and elements to add them in new groups
6982
6983   vector< const SMDS_MeshElement* > resultElems;
6984   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6985   {
6986     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6987     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6988     if ( gens.Length() != elems.Length() )
6989       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6990
6991     // loop on created elements
6992     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6993     {
6994       const SMDS_MeshElement* sourceElem = gens( iElem );
6995       if ( !sourceElem ) {
6996         MESSAGE("generateGroups(): NULL source element");
6997         continue;
6998       }
6999       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7000       if ( groupsOldNew.empty() ) { // no groups of this type at all
7001         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7002           ++iElem; // skip all elements made by sourceElem
7003         continue;
7004       }
7005       // collect all elements made by the iElem-th sourceElem
7006       resultElems.clear();
7007       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7008         if ( resElem != sourceElem )
7009           resultElems.push_back( resElem );
7010       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7011         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7012           if ( resElem != sourceElem )
7013             resultElems.push_back( resElem );
7014
7015       const SMDS_MeshElement* topElem = 0;
7016       if ( isNodes ) // there must be a top element
7017       {
7018         topElem = resultElems.back();
7019         resultElems.pop_back();
7020       }
7021       else
7022       {
7023         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7024         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7025           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7026           {
7027             topElem = *resElemIt;
7028             *resElemIt = 0; // erase *resElemIt
7029             break;
7030           }
7031       }
7032       // add resultElems to groups originted from ones the sourceElem belongs to
7033       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7034       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7035       {
7036         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7037         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7038         {
7039           // fill in a new group
7040           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7041           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7042           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7043             if ( *resElemIt )
7044               newGroup.Add( *resElemIt );
7045
7046           // fill a "top" group
7047           if ( topElem )
7048           {
7049             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7050             newTopGroup.Add( topElem );
7051          }
7052         }
7053       }
7054     } // loop on created elements
7055   }// loop on nodes and elements
7056
7057   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7058
7059   list<int> topGrouIds;
7060   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7061   {
7062     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7063     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7064                                       orderedOldNewGroups[i]->get<2>() };
7065     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7066     {
7067       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7068       if ( newGroupDS->IsEmpty() )
7069       {
7070         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7071       }
7072       else
7073       {
7074         // set group type
7075         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7076
7077         // make a name
7078         const bool isTop = ( topPresent &&
7079                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7080                              is2nd );
7081
7082         string name = oldGroupDS->GetStoreName();
7083         { // remove trailing whitespaces (issue 22599)
7084           size_t size = name.size();
7085           while ( size > 1 && isspace( name[ size-1 ]))
7086             --size;
7087           if ( size != name.size() )
7088           {
7089             name.resize( size );
7090             oldGroupDS->SetStoreName( name.c_str() );
7091           }
7092         }
7093         if ( !targetMesh ) {
7094           string suffix = ( isTop ? "top": postfix.c_str() );
7095           name += "_";
7096           name += suffix;
7097           int nb = 1;
7098           while ( !groupNames.insert( name ).second ) // name exists
7099             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7100         }
7101         else if ( isTop ) {
7102           name += "_top";
7103         }
7104         newGroupDS->SetStoreName( name.c_str() );
7105
7106         // make a SMESH_Groups
7107         mesh->AddGroup( newGroupDS );
7108         if ( isTop )
7109           topGrouIds.push_back( newGroupDS->GetID() );
7110         else
7111           newGroupIDs->push_back( newGroupDS->GetID() );
7112       }
7113     }
7114   }
7115   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7116
7117   return newGroupIDs;
7118 }
7119
7120 //================================================================================
7121 /*!
7122  *  * \brief Return list of group of nodes close to each other within theTolerance
7123  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7124  *  *        an Octree algorithm
7125  *  \param [in,out] theNodes - the nodes to treat
7126  *  \param [in]     theTolerance - the tolerance
7127  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7128  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7129  *         corner and medium nodes in separate groups
7130  */
7131 //================================================================================
7132
7133 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7134                                             const double         theTolerance,
7135                                             TListOfListOfNodes & theGroupsOfNodes,
7136                                             bool                 theSeparateCornersAndMedium)
7137 {
7138   myLastCreatedElems.Clear();
7139   myLastCreatedNodes.Clear();
7140
7141   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7142        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7143        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7144     theSeparateCornersAndMedium = false;
7145
7146   TIDSortedNodeSet& corners = theNodes;
7147   TIDSortedNodeSet  medium;
7148
7149   if ( theNodes.empty() ) // get all nodes in the mesh
7150   {
7151     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7152     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7153     if ( theSeparateCornersAndMedium )
7154       while ( nIt->more() )
7155       {
7156         const SMDS_MeshNode* n = nIt->next();
7157         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7158         nodeSet->insert( nodeSet->end(), n );
7159       }
7160     else
7161       while ( nIt->more() )
7162         theNodes.insert( theNodes.end(),nIt->next() );
7163   }
7164   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7165   {
7166     TIDSortedNodeSet::iterator nIt = corners.begin();
7167     while ( nIt != corners.end() )
7168       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7169       {
7170         medium.insert( medium.end(), *nIt );
7171         corners.erase( nIt++ );
7172       }
7173       else
7174       {
7175         ++nIt;
7176       }
7177   }
7178
7179   if ( !corners.empty() )
7180     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7181   if ( !medium.empty() )
7182     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7183 }
7184
7185 //=======================================================================
7186 //function : SimplifyFace
7187 //purpose  : split a chain of nodes into several closed chains
7188 //=======================================================================
7189
7190 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7191                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7192                                     vector<int>&                         quantities) const
7193 {
7194   int nbNodes = faceNodes.size();
7195   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7196     --nbNodes;
7197   if ( nbNodes < 3 )
7198     return 0;
7199   size_t prevNbQuant = quantities.size();
7200
7201   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7202   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7203   map< const SMDS_MeshNode*, int >::iterator nInd;
7204
7205   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7206   simpleNodes.push_back( faceNodes[0] );
7207   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7208   {
7209     if ( faceNodes[ iCur ] != simpleNodes.back() )
7210     {
7211       int index = simpleNodes.size();
7212       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7213       int prevIndex = nInd->second;
7214       if ( prevIndex < index )
7215       {
7216         // a sub-loop found
7217         int loopLen = index - prevIndex;
7218         if ( loopLen > 2 )
7219         {
7220           // store the sub-loop
7221           quantities.push_back( loopLen );
7222           for ( int i = prevIndex; i < index; i++ )
7223             poly_nodes.push_back( simpleNodes[ i ]);
7224         }
7225         simpleNodes.resize( prevIndex+1 );
7226       }
7227       else
7228       {
7229         simpleNodes.push_back( faceNodes[ iCur ]);
7230       }
7231     }
7232   }
7233
7234   if ( simpleNodes.size() > 2 )
7235   {
7236     quantities.push_back( simpleNodes.size() );
7237     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7238   }
7239
7240   return quantities.size() - prevNbQuant;
7241 }
7242
7243 //=======================================================================
7244 //function : MergeNodes
7245 //purpose  : In each group, the cdr of nodes are substituted by the first one
7246 //           in all elements.
7247 //=======================================================================
7248
7249 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7250 {
7251   myLastCreatedElems.Clear();
7252   myLastCreatedNodes.Clear();
7253
7254   SMESHDS_Mesh* aMesh = GetMeshDS();
7255
7256   TNodeNodeMap nodeNodeMap; // node to replace - new node
7257   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7258   list< int > rmElemIds, rmNodeIds;
7259
7260   // Fill nodeNodeMap and elems
7261
7262   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7263   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7264   {
7265     list<const SMDS_MeshNode*>& nodes = *grIt;
7266     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7267     const SMDS_MeshNode* nToKeep = *nIt;
7268     for ( ++nIt; nIt != nodes.end(); nIt++ )
7269     {
7270       const SMDS_MeshNode* nToRemove = *nIt;
7271       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7272       if ( nToRemove != nToKeep )
7273       {
7274         rmNodeIds.push_back( nToRemove->GetID() );
7275         AddToSameGroups( nToKeep, nToRemove, aMesh );
7276         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7277         // after MergeNodes() w/o creating node in place of merged ones.
7278         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7279         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7280           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7281             sm->SetIsAlwaysComputed( true );
7282       }
7283       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7284       while ( invElemIt->more() ) {
7285         const SMDS_MeshElement* elem = invElemIt->next();
7286         elems.insert(elem);
7287       }
7288     }
7289   }
7290   // Change element nodes or remove an element
7291
7292   set<const SMDS_MeshNode*> nodeSet;
7293   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7294   vector<int> iRepl;
7295   ElemFeatures elemType;
7296
7297   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7298   for ( ; eIt != elems.end(); eIt++ )
7299   {
7300     const SMDS_MeshElement* elem = *eIt;
7301     const           int  nbNodes = elem->NbNodes();
7302     const           int aShapeId = FindShape( elem );
7303     SMDSAbs_EntityType    entity = elem->GetEntityType();
7304
7305     nodeSet.clear();
7306     curNodes.resize( nbNodes );
7307     uniqueNodes.resize( nbNodes );
7308     iRepl.resize( nbNodes );
7309     int iUnique = 0, iCur = 0, nbRepl = 0;
7310
7311     // get new seq of nodes
7312     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7313     while ( itN->more() )
7314     {
7315       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7316
7317       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7318       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7319         n = (*nnIt).second;
7320         { ////////// BUG 0020185: begin
7321           bool stopRecur = false;
7322           set<const SMDS_MeshNode*> nodesRecur;
7323           nodesRecur.insert(n);
7324           while (!stopRecur) {
7325             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7326             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7327               n = (*nnIt_i).second;
7328               if (!nodesRecur.insert(n).second) {
7329                 // error: recursive dependency
7330                 stopRecur = true;
7331               }
7332             }
7333             else
7334               stopRecur = true;
7335           }
7336         } ////////// BUG 0020185: end
7337       }
7338       curNodes[ iCur ] = n;
7339       bool isUnique = nodeSet.insert( n ).second;
7340       if ( isUnique )
7341         uniqueNodes[ iUnique++ ] = n;
7342       else
7343         iRepl[ nbRepl++ ] = iCur;
7344       iCur++;
7345     }
7346
7347     // Analyse element topology after replacement
7348
7349     bool isOk = true;
7350     int nbUniqueNodes = nodeSet.size();
7351     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7352     {
7353       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7354       {
7355         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7356         {
7357           elemType.Init( elem );
7358           const bool isQuad = elemType.myIsQuad;
7359           if ( isQuad )
7360             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7361               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7362
7363           // a polygon can divide into several elements
7364           vector<const SMDS_MeshNode *> polygons_nodes;
7365           vector<int> quantities;
7366           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7367           if (nbNew > 0)
7368           {
7369             vector<const SMDS_MeshNode *> face_nodes;
7370             int inode = 0;
7371             for (int iface = 0; iface < nbNew; iface++)
7372             {
7373               int nbNewNodes = quantities[iface];
7374               face_nodes.assign( polygons_nodes.begin() + inode,
7375                                  polygons_nodes.begin() + inode + nbNewNodes );
7376               inode += nbNewNodes;
7377               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7378               {
7379                 bool isValid = ( nbNewNodes % 2 == 0 );
7380                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7381                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7382                 elemType.SetQuad( isValid );
7383                 if ( isValid ) // put medium nodes after corners
7384                   SMDS_MeshCell::applyInterlaceRev
7385                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7386                                                           nbNewNodes ), face_nodes );
7387               }
7388               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7389
7390               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7391               if ( aShapeId )
7392                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7393             }
7394           }
7395           rmElemIds.push_back(elem->GetID());
7396
7397         } // Polygon
7398
7399         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7400         {
7401           if ( nbUniqueNodes < 4 ) {
7402             rmElemIds.push_back(elem->GetID());
7403           }
7404           else {
7405             // each face has to be analyzed in order to check volume validity
7406             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7407             if ( aPolyedre )
7408             {
7409               int nbFaces = aPolyedre->NbFaces();
7410
7411               vector<const SMDS_MeshNode *> poly_nodes;
7412               vector<int>                   quantities;
7413               vector<const SMDS_MeshNode *> faceNodes;
7414
7415               for (int iface = 1; iface <= nbFaces; iface++)
7416               {
7417                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7418                 faceNodes.resize( nbFaceNodes );
7419                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7420                 {
7421                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7422                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7423                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7424                     faceNode = (*nnIt).second;
7425                   faceNodes[inode - 1] = faceNode;
7426                 }
7427                 SimplifyFace(faceNodes, poly_nodes, quantities);
7428               }
7429
7430               if ( quantities.size() > 3 ) {
7431                 // TODO: remove coincident faces
7432               }
7433
7434               if ( quantities.size() > 3 )
7435               {
7436                 const SMDS_MeshElement* newElem =
7437                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7438                 myLastCreatedElems.Append( newElem );
7439                 if ( aShapeId && newElem )
7440                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7441                 rmElemIds.push_back( elem->GetID() );
7442               }
7443             }
7444             else {
7445               rmElemIds.push_back( elem->GetID() );
7446             }
7447           }
7448         }
7449         else {
7450         }
7451
7452         continue;
7453       } // poly element
7454
7455       // Regular elements
7456       // TODO not all the possible cases are solved. Find something more generic?
7457       switch ( entity ) {
7458       case SMDSEntity_Edge: //////// EDGE
7459       case SMDSEntity_Triangle: //// TRIANGLE
7460       case SMDSEntity_Quad_Triangle:
7461       case SMDSEntity_Tetra:
7462       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7463       {
7464         isOk = false;
7465         break;
7466       }
7467       case SMDSEntity_Quad_Edge:
7468       {
7469         isOk = false; // to linear EDGE ???????
7470         break;
7471       }
7472       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7473       {
7474         if ( nbUniqueNodes < 3 )
7475           isOk = false;
7476         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7477           isOk = false; // opposite nodes stick
7478         break;
7479       }
7480       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7481       {
7482         //   1    5    2
7483         //    +---+---+
7484         //    |       |
7485         //   4+       +6
7486         //    |       |
7487         //    +---+---+
7488         //   0    7    3
7489         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7490             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7491              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7492              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7493              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7494         {
7495           isOk = true;
7496         }
7497         break;
7498       }
7499       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7500       {
7501         //   1    5    2
7502         //    +---+---+
7503         //    |       |
7504         //   4+  8+   +6
7505         //    |       |
7506         //    +---+---+
7507         //   0    7    3
7508         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7509             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7510              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7511              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7512              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7513         {
7514           isOk = true;
7515         }
7516         break;
7517       }
7518       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7519       {
7520         isOk = false;
7521         if ( nbUniqueNodes == 4 ) {
7522           // ---------------------------------> tetrahedron
7523           if ( curNodes[3] == curNodes[4] &&
7524                curNodes[3] == curNodes[5] ) {
7525             // top nodes stick
7526             isOk = true;
7527           }
7528           else if ( curNodes[0] == curNodes[1] &&
7529                     curNodes[0] == curNodes[2] ) {
7530             // bottom nodes stick: set a top before
7531             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7532             uniqueNodes[ 0 ] = curNodes [ 5 ];
7533             uniqueNodes[ 1 ] = curNodes [ 4 ];
7534             uniqueNodes[ 2 ] = curNodes [ 3 ];
7535             isOk = true;
7536           }
7537           else if (( curNodes[0] == curNodes[3] ) +
7538                    ( curNodes[1] == curNodes[4] ) +
7539                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7540             // a lateral face turns into a line
7541             isOk = true;
7542           }
7543         }
7544         else if ( nbUniqueNodes == 5 ) {
7545           // PENTAHEDRON --------------------> pyramid
7546           if ( curNodes[0] == curNodes[3] )
7547           {
7548             uniqueNodes[ 0 ] = curNodes[ 1 ];
7549             uniqueNodes[ 1 ] = curNodes[ 4 ];
7550             uniqueNodes[ 2 ] = curNodes[ 5 ];
7551             uniqueNodes[ 3 ] = curNodes[ 2 ];
7552             uniqueNodes[ 4 ] = curNodes[ 0 ];
7553             isOk = true;
7554           }
7555           if ( curNodes[1] == curNodes[4] )
7556           {
7557             uniqueNodes[ 0 ] = curNodes[ 0 ];
7558             uniqueNodes[ 1 ] = curNodes[ 2 ];
7559             uniqueNodes[ 2 ] = curNodes[ 5 ];
7560             uniqueNodes[ 3 ] = curNodes[ 3 ];
7561             uniqueNodes[ 4 ] = curNodes[ 1 ];
7562             isOk = true;
7563           }
7564           if ( curNodes[2] == curNodes[5] )
7565           {
7566             uniqueNodes[ 0 ] = curNodes[ 0 ];
7567             uniqueNodes[ 1 ] = curNodes[ 3 ];
7568             uniqueNodes[ 2 ] = curNodes[ 4 ];
7569             uniqueNodes[ 3 ] = curNodes[ 1 ];
7570             uniqueNodes[ 4 ] = curNodes[ 2 ];
7571             isOk = true;
7572           }
7573         }
7574         break;
7575       }
7576       case SMDSEntity_Hexa:
7577       {
7578         //////////////////////////////////// HEXAHEDRON
7579         isOk = false;
7580         SMDS_VolumeTool hexa (elem);
7581         hexa.SetExternalNormal();
7582         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7583           //////////////////////// HEX ---> tetrahedron
7584           for ( int iFace = 0; iFace < 6; iFace++ ) {
7585             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7586             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7587                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7588                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7589               // one face turns into a point ...
7590               int  pickInd = ind[ 0 ];
7591               int iOppFace = hexa.GetOppFaceIndex( iFace );
7592               ind = hexa.GetFaceNodesIndices( iOppFace );
7593               int nbStick = 0;
7594               uniqueNodes.clear();
7595               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7596                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7597                   nbStick++;
7598                 else
7599                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7600               }
7601               if ( nbStick == 1 ) {
7602                 // ... and the opposite one - into a triangle.
7603                 // set a top node
7604                 uniqueNodes.push_back( curNodes[ pickInd ]);
7605                 isOk = true;
7606               }
7607               break;
7608             }
7609           }
7610         }
7611         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7612           //////////////////////// HEX ---> prism
7613           int nbTria = 0, iTria[3];
7614           const int *ind; // indices of face nodes
7615           // look for triangular faces
7616           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7617             ind = hexa.GetFaceNodesIndices( iFace );
7618             TIDSortedNodeSet faceNodes;
7619             for ( iCur = 0; iCur < 4; iCur++ )
7620               faceNodes.insert( curNodes[ind[iCur]] );
7621             if ( faceNodes.size() == 3 )
7622               iTria[ nbTria++ ] = iFace;
7623           }
7624           // check if triangles are opposite
7625           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7626           {
7627             // set nodes of the bottom triangle
7628             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7629             vector<int> indB;
7630             for ( iCur = 0; iCur < 4; iCur++ )
7631               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7632                 indB.push_back( ind[iCur] );
7633             if ( !hexa.IsForward() )
7634               std::swap( indB[0], indB[2] );
7635             for ( iCur = 0; iCur < 3; iCur++ )
7636               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7637             // set nodes of the top triangle
7638             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7639             for ( iCur = 0; iCur < 3; ++iCur )
7640               for ( int j = 0; j < 4; ++j )
7641                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7642                 {
7643                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7644                   break;
7645                 }
7646             isOk = true;
7647             break;
7648           }
7649         }
7650         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7651           //////////////////// HEXAHEDRON ---> pyramid
7652           for ( int iFace = 0; iFace < 6; iFace++ ) {
7653             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7654             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7655                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7656                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7657               // one face turns into a point ...
7658               int iOppFace = hexa.GetOppFaceIndex( iFace );
7659               ind = hexa.GetFaceNodesIndices( iOppFace );
7660               uniqueNodes.clear();
7661               for ( iCur = 0; iCur < 4; iCur++ ) {
7662                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7663                   break;
7664                 else
7665                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7666               }
7667               if ( uniqueNodes.size() == 4 ) {
7668                 // ... and the opposite one is a quadrangle
7669                 // set a top node
7670                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7671                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7672                 isOk = true;
7673               }
7674               break;
7675             }
7676           }
7677         }
7678
7679         if ( !isOk && nbUniqueNodes > 4 ) {
7680           ////////////////// HEXAHEDRON ---> polyhedron
7681           hexa.SetExternalNormal();
7682           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7683           vector<int>                   quantities; quantities.reserve( 6 );
7684           for ( int iFace = 0; iFace < 6; iFace++ )
7685           {
7686             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7687             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7688                  curNodes[ind[1]] == curNodes[ind[3]] )
7689             {
7690               quantities.clear();
7691               break; // opposite nodes stick
7692             }
7693             nodeSet.clear();
7694             for ( iCur = 0; iCur < 4; iCur++ )
7695             {
7696               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7697                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7698             }
7699             if ( nodeSet.size() < 3 )
7700               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7701             else
7702               quantities.push_back( nodeSet.size() );
7703           }
7704           if ( quantities.size() >= 4 )
7705           {
7706             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7707             myLastCreatedElems.Append( newElem );
7708             if ( aShapeId && newElem )
7709               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7710             rmElemIds.push_back( elem->GetID() );
7711           }
7712         }
7713         break;
7714       } // case HEXAHEDRON
7715
7716       default:
7717         isOk = false;
7718       } // switch ( nbNodes )
7719
7720     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7721
7722     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7723     {
7724       if ( nbNodes != nbUniqueNodes ||
7725            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7726       {
7727         elemType.Init( elem ).SetID( elem->GetID() );
7728
7729         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7730         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7731
7732         uniqueNodes.resize(nbUniqueNodes);
7733         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7734         if ( sm && newElem )
7735           sm->AddElement( newElem );
7736         if ( elem != newElem )
7737           ReplaceElemInGroups( elem, newElem, aMesh );
7738       }
7739     }
7740     else {
7741       // Remove invalid regular element or invalid polygon
7742       rmElemIds.push_back( elem->GetID() );
7743     }
7744
7745   } // loop on elements
7746
7747   // Remove bad elements, then equal nodes (order important)
7748
7749   Remove( rmElemIds, false );
7750   Remove( rmNodeIds, true );
7751
7752   return;
7753 }
7754
7755
7756 // ========================================================
7757 // class   : SortableElement
7758 // purpose : allow sorting elements basing on their nodes
7759 // ========================================================
7760 class SortableElement : public set <const SMDS_MeshElement*>
7761 {
7762 public:
7763
7764   SortableElement( const SMDS_MeshElement* theElem )
7765   {
7766     myElem = theElem;
7767     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7768     while ( nodeIt->more() )
7769       this->insert( nodeIt->next() );
7770   }
7771
7772   const SMDS_MeshElement* Get() const
7773   { return myElem; }
7774
7775 private:
7776   mutable const SMDS_MeshElement* myElem;
7777 };
7778
7779 //=======================================================================
7780 //function : FindEqualElements
7781 //purpose  : Return list of group of elements built on the same nodes.
7782 //           Search among theElements or in the whole mesh if theElements is empty
7783 //=======================================================================
7784
7785 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7786                                          TListOfListOfElementsID & theGroupsOfElementsID)
7787 {
7788   myLastCreatedElems.Clear();
7789   myLastCreatedNodes.Clear();
7790
7791   typedef map< SortableElement, int > TMapOfNodeSet;
7792   typedef list<int> TGroupOfElems;
7793
7794   if ( theElements.empty() )
7795   { // get all elements in the mesh
7796     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7797     while ( eIt->more() )
7798       theElements.insert( theElements.end(), eIt->next() );
7799   }
7800
7801   vector< TGroupOfElems > arrayOfGroups;
7802   TGroupOfElems groupOfElems;
7803   TMapOfNodeSet mapOfNodeSet;
7804
7805   TIDSortedElemSet::iterator elemIt = theElements.begin();
7806   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7807   {
7808     const SMDS_MeshElement* curElem = *elemIt;
7809     SortableElement SE(curElem);
7810     // check uniqueness
7811     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7812     if ( !pp.second ) { // one more coincident elem
7813       TMapOfNodeSet::iterator& itSE = pp.first;
7814       int ind = (*itSE).second;
7815       arrayOfGroups[ind].push_back( curElem->GetID() );
7816     }
7817     else {
7818       arrayOfGroups.push_back( groupOfElems );
7819       arrayOfGroups.back().push_back( curElem->GetID() );
7820       i++;
7821     }
7822   }
7823
7824   groupOfElems.clear();
7825   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7826   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7827   {
7828     if ( groupIt->size() > 1 ) {
7829       //groupOfElems.sort(); -- theElements is sorted already
7830       theGroupsOfElementsID.push_back( groupOfElems );
7831       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7832     }
7833   }
7834 }
7835
7836 //=======================================================================
7837 //function : MergeElements
7838 //purpose  : In each given group, substitute all elements by the first one.
7839 //=======================================================================
7840
7841 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7842 {
7843   myLastCreatedElems.Clear();
7844   myLastCreatedNodes.Clear();
7845
7846   typedef list<int> TListOfIDs;
7847   TListOfIDs rmElemIds; // IDs of elems to remove
7848
7849   SMESHDS_Mesh* aMesh = GetMeshDS();
7850
7851   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7852   while ( groupsIt != theGroupsOfElementsID.end() ) {
7853     TListOfIDs& aGroupOfElemID = *groupsIt;
7854     aGroupOfElemID.sort();
7855     int elemIDToKeep = aGroupOfElemID.front();
7856     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7857     aGroupOfElemID.pop_front();
7858     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7859     while ( idIt != aGroupOfElemID.end() ) {
7860       int elemIDToRemove = *idIt;
7861       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7862       // add the kept element in groups of removed one (PAL15188)
7863       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7864       rmElemIds.push_back( elemIDToRemove );
7865       ++idIt;
7866     }
7867     ++groupsIt;
7868   }
7869
7870   Remove( rmElemIds, false );
7871 }
7872
7873 //=======================================================================
7874 //function : MergeEqualElements
7875 //purpose  : Remove all but one of elements built on the same nodes.
7876 //=======================================================================
7877
7878 void SMESH_MeshEditor::MergeEqualElements()
7879 {
7880   TIDSortedElemSet aMeshElements; /* empty input ==
7881                                      to merge equal elements in the whole mesh */
7882   TListOfListOfElementsID aGroupsOfElementsID;
7883   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7884   MergeElements(aGroupsOfElementsID);
7885 }
7886
7887 //=======================================================================
7888 //function : findAdjacentFace
7889 //purpose  :
7890 //=======================================================================
7891
7892 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7893                                                 const SMDS_MeshNode* n2,
7894                                                 const SMDS_MeshElement* elem)
7895 {
7896   TIDSortedElemSet elemSet, avoidSet;
7897   if ( elem )
7898     avoidSet.insert ( elem );
7899   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7900 }
7901
7902 //=======================================================================
7903 //function : findSegment
7904 //purpose  : Return a mesh segment by two nodes one of which can be medium
7905 //=======================================================================
7906
7907 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7908                                            const SMDS_MeshNode* n2)
7909 {
7910   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7911   while ( it->more() )
7912   {
7913     const SMDS_MeshElement* seg = it->next();
7914     if ( seg->GetNodeIndex( n2 ) >= 0 )
7915       return seg;
7916   }
7917   return 0;
7918 }
7919
7920 //=======================================================================
7921 //function : FindFreeBorder
7922 //purpose  :
7923 //=======================================================================
7924
7925 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7926
7927 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7928                                        const SMDS_MeshNode*             theSecondNode,
7929                                        const SMDS_MeshNode*             theLastNode,
7930                                        list< const SMDS_MeshNode* > &   theNodes,
7931                                        list< const SMDS_MeshElement* >& theFaces)
7932 {
7933   if ( !theFirstNode || !theSecondNode )
7934     return false;
7935   // find border face between theFirstNode and theSecondNode
7936   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7937   if ( !curElem )
7938     return false;
7939
7940   theFaces.push_back( curElem );
7941   theNodes.push_back( theFirstNode );
7942   theNodes.push_back( theSecondNode );
7943
7944   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7945   TIDSortedElemSet foundElems;
7946   bool needTheLast = ( theLastNode != 0 );
7947
7948   while ( nStart != theLastNode ) {
7949     if ( nStart == theFirstNode )
7950       return !needTheLast;
7951
7952     // find all free border faces sharing form nStart
7953
7954     list< const SMDS_MeshElement* > curElemList;
7955     list< const SMDS_MeshNode* >    nStartList;
7956     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7957     while ( invElemIt->more() ) {
7958       const SMDS_MeshElement* e = invElemIt->next();
7959       if ( e == curElem || foundElems.insert( e ).second ) {
7960         // get nodes
7961         int iNode = 0, nbNodes = e->NbNodes();
7962         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7963
7964         if ( e->IsQuadratic() ) {
7965           const SMDS_VtkFace* F =
7966             dynamic_cast<const SMDS_VtkFace*>(e);
7967           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7968           // use special nodes iterator
7969           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7970           while( anIter->more() ) {
7971             nodes[ iNode++ ] = cast2Node(anIter->next());
7972           }
7973         }
7974         else {
7975           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7976           while ( nIt->more() )
7977             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7978         }
7979         nodes[ iNode ] = nodes[ 0 ];
7980         // check 2 links
7981         for ( iNode = 0; iNode < nbNodes; iNode++ )
7982           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7983                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7984               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7985           {
7986             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7987             curElemList.push_back( e );
7988           }
7989       }
7990     }
7991     // analyse the found
7992
7993     int nbNewBorders = curElemList.size();
7994     if ( nbNewBorders == 0 ) {
7995       // no free border furthermore
7996       return !needTheLast;
7997     }
7998     else if ( nbNewBorders == 1 ) {
7999       // one more element found
8000       nIgnore = nStart;
8001       nStart = nStartList.front();
8002       curElem = curElemList.front();
8003       theFaces.push_back( curElem );
8004       theNodes.push_back( nStart );
8005     }
8006     else {
8007       // several continuations found
8008       list< const SMDS_MeshElement* >::iterator curElemIt;
8009       list< const SMDS_MeshNode* >::iterator nStartIt;
8010       // check if one of them reached the last node
8011       if ( needTheLast ) {
8012         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8013              curElemIt!= curElemList.end();
8014              curElemIt++, nStartIt++ )
8015           if ( *nStartIt == theLastNode ) {
8016             theFaces.push_back( *curElemIt );
8017             theNodes.push_back( *nStartIt );
8018             return true;
8019           }
8020       }
8021       // find the best free border by the continuations
8022       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8023       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8024       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8025            curElemIt!= curElemList.end();
8026            curElemIt++, nStartIt++ )
8027       {
8028         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8029         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8030         // find one more free border
8031         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8032           cNL->clear();
8033           cFL->clear();
8034         }
8035         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8036           // choice: clear a worse one
8037           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8038           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8039           contNodes[ iWorse ].clear();
8040           contFaces[ iWorse ].clear();
8041         }
8042       }
8043       if ( contNodes[0].empty() && contNodes[1].empty() )
8044         return false;
8045
8046       // append the best free border
8047       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8048       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8049       theNodes.pop_back(); // remove nIgnore
8050       theNodes.pop_back(); // remove nStart
8051       theFaces.pop_back(); // remove curElem
8052       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8053       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8054       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8055       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8056       return true;
8057
8058     } // several continuations found
8059   } // while ( nStart != theLastNode )
8060
8061   return true;
8062 }
8063
8064 //=======================================================================
8065 //function : CheckFreeBorderNodes
8066 //purpose  : Return true if the tree nodes are on a free border
8067 //=======================================================================
8068
8069 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8070                                             const SMDS_MeshNode* theNode2,
8071                                             const SMDS_MeshNode* theNode3)
8072 {
8073   list< const SMDS_MeshNode* > nodes;
8074   list< const SMDS_MeshElement* > faces;
8075   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8076 }
8077
8078 //=======================================================================
8079 //function : SewFreeBorder
8080 //purpose  :
8081 //warning  : for border-to-side sewing theSideSecondNode is considered as
8082 //           the last side node and theSideThirdNode is not used
8083 //=======================================================================
8084
8085 SMESH_MeshEditor::Sew_Error
8086 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8087                                  const SMDS_MeshNode* theBordSecondNode,
8088                                  const SMDS_MeshNode* theBordLastNode,
8089                                  const SMDS_MeshNode* theSideFirstNode,
8090                                  const SMDS_MeshNode* theSideSecondNode,
8091                                  const SMDS_MeshNode* theSideThirdNode,
8092                                  const bool           theSideIsFreeBorder,
8093                                  const bool           toCreatePolygons,
8094                                  const bool           toCreatePolyedrs)
8095 {
8096   myLastCreatedElems.Clear();
8097   myLastCreatedNodes.Clear();
8098
8099   Sew_Error aResult = SEW_OK;
8100
8101   // ====================================
8102   //    find side nodes and elements
8103   // ====================================
8104
8105   list< const SMDS_MeshNode* >    nSide[ 2 ];
8106   list< const SMDS_MeshElement* > eSide[ 2 ];
8107   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8108   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8109
8110   // Free border 1
8111   // --------------
8112   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8113                       nSide[0], eSide[0])) {
8114     MESSAGE(" Free Border 1 not found " );
8115     aResult = SEW_BORDER1_NOT_FOUND;
8116   }
8117   if (theSideIsFreeBorder) {
8118     // Free border 2
8119     // --------------
8120     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8121                         nSide[1], eSide[1])) {
8122       MESSAGE(" Free Border 2 not found " );
8123       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8124     }
8125   }
8126   if ( aResult != SEW_OK )
8127     return aResult;
8128
8129   if (!theSideIsFreeBorder) {
8130     // Side 2
8131     // --------------
8132
8133     // -------------------------------------------------------------------------
8134     // Algo:
8135     // 1. If nodes to merge are not coincident, move nodes of the free border
8136     //    from the coord sys defined by the direction from the first to last
8137     //    nodes of the border to the correspondent sys of the side 2
8138     // 2. On the side 2, find the links most co-directed with the correspondent
8139     //    links of the free border
8140     // -------------------------------------------------------------------------
8141
8142     // 1. Since sewing may break if there are volumes to split on the side 2,
8143     //    we wont move nodes but just compute new coordinates for them
8144     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8145     TNodeXYZMap nBordXYZ;
8146     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8147     list< const SMDS_MeshNode* >::iterator nBordIt;
8148
8149     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8150     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8151     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8152     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8153     double tol2 = 1.e-8;
8154     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8155     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8156       // Need node movement.
8157
8158       // find X and Z axes to create trsf
8159       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8160       gp_Vec X = Zs ^ Zb;
8161       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8162         // Zb || Zs
8163         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8164
8165       // coord systems
8166       gp_Ax3 toBordAx( Pb1, Zb, X );
8167       gp_Ax3 fromSideAx( Ps1, Zs, X );
8168       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8169       // set trsf
8170       gp_Trsf toBordSys, fromSide2Sys;
8171       toBordSys.SetTransformation( toBordAx );
8172       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8173       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8174
8175       // move
8176       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8177         const SMDS_MeshNode* n = *nBordIt;
8178         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8179         toBordSys.Transforms( xyz );
8180         fromSide2Sys.Transforms( xyz );
8181         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8182       }
8183     }
8184     else {
8185       // just insert nodes XYZ in the nBordXYZ map
8186       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8187         const SMDS_MeshNode* n = *nBordIt;
8188         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8189       }
8190     }
8191
8192     // 2. On the side 2, find the links most co-directed with the correspondent
8193     //    links of the free border
8194
8195     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8196     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8197     sideNodes.push_back( theSideFirstNode );
8198
8199     bool hasVolumes = false;
8200     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8201     set<long> foundSideLinkIDs, checkedLinkIDs;
8202     SMDS_VolumeTool volume;
8203     //const SMDS_MeshNode* faceNodes[ 4 ];
8204
8205     const SMDS_MeshNode*    sideNode;
8206     const SMDS_MeshElement* sideElem  = 0;
8207     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8208     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8209     nBordIt = bordNodes.begin();
8210     nBordIt++;
8211     // border node position and border link direction to compare with
8212     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8213     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8214     // choose next side node by link direction or by closeness to
8215     // the current border node:
8216     bool searchByDir = ( *nBordIt != theBordLastNode );
8217     do {
8218       // find the next node on the Side 2
8219       sideNode = 0;
8220       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8221       long linkID;
8222       checkedLinkIDs.clear();
8223       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8224
8225       // loop on inverse elements of current node (prevSideNode) on the Side 2
8226       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8227       while ( invElemIt->more() )
8228       {
8229         const SMDS_MeshElement* elem = invElemIt->next();
8230         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8231         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8232         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8233         bool isVolume = volume.Set( elem );
8234         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8235         if ( isVolume ) // --volume
8236           hasVolumes = true;
8237         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8238           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8239           if(elem->IsQuadratic()) {
8240             const SMDS_VtkFace* F =
8241               dynamic_cast<const SMDS_VtkFace*>(elem);
8242             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8243             // use special nodes iterator
8244             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8245             while( anIter->more() ) {
8246               nodes[ iNode ] = cast2Node(anIter->next());
8247               if ( nodes[ iNode++ ] == prevSideNode )
8248                 iPrevNode = iNode - 1;
8249             }
8250           }
8251           else {
8252             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8253             while ( nIt->more() ) {
8254               nodes[ iNode ] = cast2Node( nIt->next() );
8255               if ( nodes[ iNode++ ] == prevSideNode )
8256                 iPrevNode = iNode - 1;
8257             }
8258           }
8259           // there are 2 links to check
8260           nbNodes = 2;
8261         }
8262         else // --edge
8263           continue;
8264         // loop on links, to be precise, on the second node of links
8265         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8266           const SMDS_MeshNode* n = nodes[ iNode ];
8267           if ( isVolume ) {
8268             if ( !volume.IsLinked( n, prevSideNode ))
8269               continue;
8270           }
8271           else {
8272             if ( iNode ) // a node before prevSideNode
8273               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8274             else         // a node after prevSideNode
8275               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8276           }
8277           // check if this link was already used
8278           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8279           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8280           if (!isJustChecked &&
8281               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8282           {
8283             // test a link geometrically
8284             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8285             bool linkIsBetter = false;
8286             double dot = 0.0, dist = 0.0;
8287             if ( searchByDir ) { // choose most co-directed link
8288               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8289               linkIsBetter = ( dot > maxDot );
8290             }
8291             else { // choose link with the node closest to bordPos
8292               dist = ( nextXYZ - bordPos ).SquareModulus();
8293               linkIsBetter = ( dist < minDist );
8294             }
8295             if ( linkIsBetter ) {
8296               maxDot = dot;
8297               minDist = dist;
8298               linkID = iLink;
8299               sideNode = n;
8300               sideElem = elem;
8301             }
8302           }
8303         }
8304       } // loop on inverse elements of prevSideNode
8305
8306       if ( !sideNode ) {
8307         MESSAGE(" Cant find path by links of the Side 2 ");
8308         return SEW_BAD_SIDE_NODES;
8309       }
8310       sideNodes.push_back( sideNode );
8311       sideElems.push_back( sideElem );
8312       foundSideLinkIDs.insert ( linkID );
8313       prevSideNode = sideNode;
8314
8315       if ( *nBordIt == theBordLastNode )
8316         searchByDir = false;
8317       else {
8318         // find the next border link to compare with
8319         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8320         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8321         // move to next border node if sideNode is before forward border node (bordPos)
8322         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8323           prevBordNode = *nBordIt;
8324           nBordIt++;
8325           bordPos = nBordXYZ[ *nBordIt ];
8326           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8327           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8328         }
8329       }
8330     }
8331     while ( sideNode != theSideSecondNode );
8332
8333     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8334       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8335       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8336     }
8337   } // end nodes search on the side 2
8338
8339   // ============================
8340   // sew the border to the side 2
8341   // ============================
8342
8343   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8344   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8345
8346   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8347   if ( toMergeConformal && toCreatePolygons )
8348   {
8349     // do not merge quadrangles if polygons are OK (IPAL0052824)
8350     eIt[0] = eSide[0].begin();
8351     eIt[1] = eSide[1].begin();
8352     bool allQuads[2] = { true, true };
8353     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8354       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8355         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8356     }
8357     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8358   }
8359
8360   TListOfListOfNodes nodeGroupsToMerge;
8361   if (( toMergeConformal ) ||
8362       ( theSideIsFreeBorder && !theSideThirdNode )) {
8363
8364     // all nodes are to be merged
8365
8366     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8367          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8368          nIt[0]++, nIt[1]++ )
8369     {
8370       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8371       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8372       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8373     }
8374   }
8375   else {
8376
8377     // insert new nodes into the border and the side to get equal nb of segments
8378
8379     // get normalized parameters of nodes on the borders
8380     vector< double > param[ 2 ];
8381     param[0].resize( maxNbNodes );
8382     param[1].resize( maxNbNodes );
8383     int iNode, iBord;
8384     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8385       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8386       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8387       const SMDS_MeshNode* nPrev = *nIt;
8388       double bordLength = 0;
8389       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8390         const SMDS_MeshNode* nCur = *nIt;
8391         gp_XYZ segment (nCur->X() - nPrev->X(),
8392                         nCur->Y() - nPrev->Y(),
8393                         nCur->Z() - nPrev->Z());
8394         double segmentLen = segment.Modulus();
8395         bordLength += segmentLen;
8396         param[ iBord ][ iNode ] = bordLength;
8397         nPrev = nCur;
8398       }
8399       // normalize within [0,1]
8400       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8401         param[ iBord ][ iNode ] /= bordLength;
8402       }
8403     }
8404
8405     // loop on border segments
8406     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8407     int i[ 2 ] = { 0, 0 };
8408     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8409     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8410
8411     TElemOfNodeListMap insertMap;
8412     TElemOfNodeListMap::iterator insertMapIt;
8413     // insertMap is
8414     // key:   elem to insert nodes into
8415     // value: 2 nodes to insert between + nodes to be inserted
8416     do {
8417       bool next[ 2 ] = { false, false };
8418
8419       // find min adjacent segment length after sewing
8420       double nextParam = 10., prevParam = 0;
8421       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8422         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8423           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8424         if ( i[ iBord ] > 0 )
8425           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8426       }
8427       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8428       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8429       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8430
8431       // choose to insert or to merge nodes
8432       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8433       if ( Abs( du ) <= minSegLen * 0.2 ) {
8434         // merge
8435         // ------
8436         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8437         const SMDS_MeshNode* n0 = *nIt[0];
8438         const SMDS_MeshNode* n1 = *nIt[1];
8439         nodeGroupsToMerge.back().push_back( n1 );
8440         nodeGroupsToMerge.back().push_back( n0 );
8441         // position of node of the border changes due to merge
8442         param[ 0 ][ i[0] ] += du;
8443         // move n1 for the sake of elem shape evaluation during insertion.
8444         // n1 will be removed by MergeNodes() anyway
8445         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8446         next[0] = next[1] = true;
8447       }
8448       else {
8449         // insert
8450         // ------
8451         int intoBord = ( du < 0 ) ? 0 : 1;
8452         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8453         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8454         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8455         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8456         if ( intoBord == 1 ) {
8457           // move node of the border to be on a link of elem of the side
8458           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8459           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8460           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8461           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8462           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8463         }
8464         insertMapIt = insertMap.find( elem );
8465         bool  notFound = ( insertMapIt == insertMap.end() );
8466         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8467         if ( otherLink ) {
8468           // insert into another link of the same element:
8469           // 1. perform insertion into the other link of the elem
8470           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8471           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8472           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8473           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8474           // 2. perform insertion into the link of adjacent faces
8475           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8476             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8477           }
8478           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8479             InsertNodesIntoLink( seg, n12, n22, nodeList );
8480           }
8481           if (toCreatePolyedrs) {
8482             // perform insertion into the links of adjacent volumes
8483             UpdateVolumes(n12, n22, nodeList);
8484           }
8485           // 3. find an element appeared on n1 and n2 after the insertion
8486           insertMap.erase( elem );
8487           elem = findAdjacentFace( n1, n2, 0 );
8488         }
8489         if ( notFound || otherLink ) {
8490           // add element and nodes of the side into the insertMap
8491           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8492           (*insertMapIt).second.push_back( n1 );
8493           (*insertMapIt).second.push_back( n2 );
8494         }
8495         // add node to be inserted into elem
8496         (*insertMapIt).second.push_back( nIns );
8497         next[ 1 - intoBord ] = true;
8498       }
8499
8500       // go to the next segment
8501       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8502         if ( next[ iBord ] ) {
8503           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8504             eIt[ iBord ]++;
8505           nPrev[ iBord ] = *nIt[ iBord ];
8506           nIt[ iBord ]++; i[ iBord ]++;
8507         }
8508       }
8509     }
8510     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8511
8512     // perform insertion of nodes into elements
8513
8514     for (insertMapIt = insertMap.begin();
8515          insertMapIt != insertMap.end();
8516          insertMapIt++ )
8517     {
8518       const SMDS_MeshElement* elem = (*insertMapIt).first;
8519       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8520       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8521       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8522
8523       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8524
8525       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8526         InsertNodesIntoLink( seg, n1, n2, nodeList );
8527       }
8528
8529       if ( !theSideIsFreeBorder ) {
8530         // look for and insert nodes into the faces adjacent to elem
8531         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8532           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8533         }
8534       }
8535       if (toCreatePolyedrs) {
8536         // perform insertion into the links of adjacent volumes
8537         UpdateVolumes(n1, n2, nodeList);
8538       }
8539     }
8540   } // end: insert new nodes
8541
8542   MergeNodes ( nodeGroupsToMerge );
8543
8544
8545   // Remove coincident segments
8546
8547   // get new segments
8548   TIDSortedElemSet segments;
8549   SMESH_SequenceOfElemPtr newFaces;
8550   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8551   {
8552     if ( !myLastCreatedElems(i) ) continue;
8553     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8554       segments.insert( segments.end(), myLastCreatedElems(i) );
8555     else
8556       newFaces.Append( myLastCreatedElems(i) );
8557   }
8558   // get segments adjacent to merged nodes
8559   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8560   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8561   {
8562     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8563     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8564     while ( segIt->more() )
8565       segments.insert( segIt->next() );
8566   }
8567
8568   // find coincident
8569   TListOfListOfElementsID equalGroups;
8570   if ( !segments.empty() )
8571     FindEqualElements( segments, equalGroups );
8572   if ( !equalGroups.empty() )
8573   {
8574     // remove from segments those that will be removed
8575     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8576     for ( ; itGroups != equalGroups.end(); ++itGroups )
8577     {
8578       list< int >& group = *itGroups;
8579       list< int >::iterator id = group.begin();
8580       for ( ++id; id != group.end(); ++id )
8581         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8582           segments.erase( seg );
8583     }
8584     // remove equal segments
8585     MergeElements( equalGroups );
8586
8587     // restore myLastCreatedElems
8588     myLastCreatedElems = newFaces;
8589     TIDSortedElemSet::iterator seg = segments.begin();
8590     for ( ; seg != segments.end(); ++seg )
8591       myLastCreatedElems.Append( *seg );
8592   }
8593
8594   return aResult;
8595 }
8596
8597 //=======================================================================
8598 //function : InsertNodesIntoLink
8599 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8600 //           and theBetweenNode2 and split theElement
8601 //=======================================================================
8602
8603 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8604                                            const SMDS_MeshNode*        theBetweenNode1,
8605                                            const SMDS_MeshNode*        theBetweenNode2,
8606                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8607                                            const bool                  toCreatePoly)
8608 {
8609   if ( !theElement ) return;
8610
8611   SMESHDS_Mesh *aMesh = GetMeshDS();
8612   vector<const SMDS_MeshElement*> newElems;
8613
8614   if ( theElement->GetType() == SMDSAbs_Edge )
8615   {
8616     theNodesToInsert.push_front( theBetweenNode1 );
8617     theNodesToInsert.push_back ( theBetweenNode2 );
8618     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8619     const SMDS_MeshNode* n1 = *n;
8620     for ( ++n; n != theNodesToInsert.end(); ++n )
8621     {
8622       const SMDS_MeshNode* n2 = *n;
8623       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8624         AddToSameGroups( seg, theElement, aMesh );
8625       else
8626         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8627       n1 = n2;
8628     }
8629     theNodesToInsert.pop_front();
8630     theNodesToInsert.pop_back();
8631
8632     if ( theElement->IsQuadratic() ) // add a not split part
8633     {
8634       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8635                                           theElement->end_nodes() );
8636       int iOther = 0, nbN = nodes.size();
8637       for ( ; iOther < nbN; ++iOther )
8638         if ( nodes[iOther] != theBetweenNode1 &&
8639              nodes[iOther] != theBetweenNode2 )
8640           break;
8641       if      ( iOther == 0 )
8642       {
8643         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8644           AddToSameGroups( seg, theElement, aMesh );
8645         else
8646           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8647       }
8648       else if ( iOther == 2 )
8649       {
8650         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8651           AddToSameGroups( seg, theElement, aMesh );
8652         else
8653           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8654       }
8655     }
8656     // treat new elements
8657     for ( size_t i = 0; i < newElems.size(); ++i )
8658       if ( newElems[i] )
8659       {
8660         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8661         myLastCreatedElems.Append( newElems[i] );
8662       }
8663     ReplaceElemInGroups( theElement, newElems, aMesh );
8664     aMesh->RemoveElement( theElement );
8665     return;
8666
8667   } // if ( theElement->GetType() == SMDSAbs_Edge )
8668
8669   const SMDS_MeshElement* theFace = theElement;
8670   if ( theFace->GetType() != SMDSAbs_Face ) return;
8671
8672   // find indices of 2 link nodes and of the rest nodes
8673   int iNode = 0, il1, il2, i3, i4;
8674   il1 = il2 = i3 = i4 = -1;
8675   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8676
8677   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8678   while ( nodeIt->more() ) {
8679     const SMDS_MeshNode* n = nodeIt->next();
8680     if ( n == theBetweenNode1 )
8681       il1 = iNode;
8682     else if ( n == theBetweenNode2 )
8683       il2 = iNode;
8684     else if ( i3 < 0 )
8685       i3 = iNode;
8686     else
8687       i4 = iNode;
8688     nodes[ iNode++ ] = n;
8689   }
8690   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8691     return ;
8692
8693   // arrange link nodes to go one after another regarding the face orientation
8694   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8695   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8696   if ( reverse ) {
8697     iNode = il1;
8698     il1 = il2;
8699     il2 = iNode;
8700     aNodesToInsert.reverse();
8701   }
8702   // check that not link nodes of a quadrangles are in good order
8703   int nbFaceNodes = theFace->NbNodes();
8704   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8705     iNode = i3;
8706     i3 = i4;
8707     i4 = iNode;
8708   }
8709
8710   if (toCreatePoly || theFace->IsPoly()) {
8711
8712     iNode = 0;
8713     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8714
8715     // add nodes of face up to first node of link
8716     bool isFLN = false;
8717
8718     if ( theFace->IsQuadratic() ) {
8719       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8720       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8721       // use special nodes iterator
8722       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8723       while( anIter->more()  && !isFLN ) {
8724         const SMDS_MeshNode* n = cast2Node(anIter->next());
8725         poly_nodes[iNode++] = n;
8726         if (n == nodes[il1]) {
8727           isFLN = true;
8728         }
8729       }
8730       // add nodes to insert
8731       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732       for (; nIt != aNodesToInsert.end(); nIt++) {
8733         poly_nodes[iNode++] = *nIt;
8734       }
8735       // add nodes of face starting from last node of link
8736       while ( anIter->more() ) {
8737         poly_nodes[iNode++] = cast2Node(anIter->next());
8738       }
8739     }
8740     else {
8741       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8742       while ( nodeIt->more() && !isFLN ) {
8743         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8744         poly_nodes[iNode++] = n;
8745         if (n == nodes[il1]) {
8746           isFLN = true;
8747         }
8748       }
8749       // add nodes to insert
8750       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8751       for (; nIt != aNodesToInsert.end(); nIt++) {
8752         poly_nodes[iNode++] = *nIt;
8753       }
8754       // add nodes of face starting from last node of link
8755       while ( nodeIt->more() ) {
8756         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8757         poly_nodes[iNode++] = n;
8758       }
8759     }
8760
8761     // make a new face
8762     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8763   }
8764
8765   else if ( !theFace->IsQuadratic() )
8766   {
8767     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8768     int nbLinkNodes = 2 + aNodesToInsert.size();
8769     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8770     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8771     linkNodes[ 0 ] = nodes[ il1 ];
8772     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8773     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8774     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8775       linkNodes[ iNode++ ] = *nIt;
8776     }
8777     // decide how to split a quadrangle: compare possible variants
8778     // and choose which of splits to be a quadrangle
8779     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8780     if ( nbFaceNodes == 3 ) {
8781       iBestQuad = nbSplits;
8782       i4 = i3;
8783     }
8784     else if ( nbFaceNodes == 4 ) {
8785       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8786       double aBestRate = DBL_MAX;
8787       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8788         i1 = 0; i2 = 1;
8789         double aBadRate = 0;
8790         // evaluate elements quality
8791         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8792           if ( iSplit == iQuad ) {
8793             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8794                                    linkNodes[ i2++ ],
8795                                    nodes[ i3 ],
8796                                    nodes[ i4 ]);
8797             aBadRate += getBadRate( &quad, aCrit );
8798           }
8799           else {
8800             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8801                                    linkNodes[ i2++ ],
8802                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8803             aBadRate += getBadRate( &tria, aCrit );
8804           }
8805         }
8806         // choice
8807         if ( aBadRate < aBestRate ) {
8808           iBestQuad = iQuad;
8809           aBestRate = aBadRate;
8810         }
8811       }
8812     }
8813
8814     // create new elements
8815     i1 = 0; i2 = 1;
8816     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8817     {
8818       if ( iSplit == iBestQuad )
8819         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8820                                             linkNodes[ i2++ ],
8821                                             nodes[ i3 ],
8822                                             nodes[ i4 ]));
8823       else
8824         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8825                                             linkNodes[ i2++ ],
8826                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8827     }
8828
8829     const SMDS_MeshNode* newNodes[ 4 ];
8830     newNodes[ 0 ] = linkNodes[ i1 ];
8831     newNodes[ 1 ] = linkNodes[ i2 ];
8832     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8833     newNodes[ 3 ] = nodes[ i4 ];
8834     if (iSplit == iBestQuad)
8835       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8836     else
8837       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8838
8839   } // end if(!theFace->IsQuadratic())
8840
8841   else { // theFace is quadratic
8842     // we have to split theFace on simple triangles and one simple quadrangle
8843     int tmp = il1/2;
8844     int nbshift = tmp*2;
8845     // shift nodes in nodes[] by nbshift
8846     int i,j;
8847     for(i=0; i<nbshift; i++) {
8848       const SMDS_MeshNode* n = nodes[0];
8849       for(j=0; j<nbFaceNodes-1; j++) {
8850         nodes[j] = nodes[j+1];
8851       }
8852       nodes[nbFaceNodes-1] = n;
8853     }
8854     il1 = il1 - nbshift;
8855     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8856     //   n0      n1     n2    n0      n1     n2
8857     //     +-----+-----+        +-----+-----+
8858     //      \         /         |           |
8859     //       \       /          |           |
8860     //      n5+     +n3       n7+           +n3
8861     //         \   /            |           |
8862     //          \ /             |           |
8863     //           +              +-----+-----+
8864     //           n4           n6      n5     n4
8865
8866     // create new elements
8867     int n1,n2,n3;
8868     if ( nbFaceNodes == 6 ) { // quadratic triangle
8869       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8870       if ( theFace->IsMediumNode(nodes[il1]) ) {
8871         // create quadrangle
8872         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8873         n1 = 1;
8874         n2 = 2;
8875         n3 = 3;
8876       }
8877       else {
8878         // create quadrangle
8879         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8880         n1 = 0;
8881         n2 = 1;
8882         n3 = 5;
8883       }
8884     }
8885     else { // nbFaceNodes==8 - quadratic quadrangle
8886       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8887       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8888       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8889       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8890         // create quadrangle
8891         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8892         n1 = 1;
8893         n2 = 2;
8894         n3 = 3;
8895       }
8896       else {
8897         // create quadrangle
8898         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8899         n1 = 0;
8900         n2 = 1;
8901         n3 = 7;
8902       }
8903     }
8904     // create needed triangles using n1,n2,n3 and inserted nodes
8905     int nbn = 2 + aNodesToInsert.size();
8906     vector<const SMDS_MeshNode*> aNodes(nbn);
8907     aNodes[0    ] = nodes[n1];
8908     aNodes[nbn-1] = nodes[n2];
8909     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8910     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8911       aNodes[iNode++] = *nIt;
8912     }
8913     for ( i = 1; i < nbn; i++ )
8914       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8915   }
8916
8917   // remove the old face
8918   for ( size_t i = 0; i < newElems.size(); ++i )
8919     if ( newElems[i] )
8920     {
8921       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8922       myLastCreatedElems.Append( newElems[i] );
8923     }
8924   ReplaceElemInGroups( theFace, newElems, aMesh );
8925   aMesh->RemoveElement(theFace);
8926
8927 } // InsertNodesIntoLink()
8928
8929 //=======================================================================
8930 //function : UpdateVolumes
8931 //purpose  :
8932 //=======================================================================
8933
8934 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8935                                       const SMDS_MeshNode*        theBetweenNode2,
8936                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8937 {
8938   myLastCreatedElems.Clear();
8939   myLastCreatedNodes.Clear();
8940
8941   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8942   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8943     const SMDS_MeshElement* elem = invElemIt->next();
8944
8945     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8946     SMDS_VolumeTool aVolume (elem);
8947     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8948       continue;
8949
8950     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8951     int iface, nbFaces = aVolume.NbFaces();
8952     vector<const SMDS_MeshNode *> poly_nodes;
8953     vector<int> quantities (nbFaces);
8954
8955     for (iface = 0; iface < nbFaces; iface++) {
8956       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8957       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8958       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8959
8960       for (int inode = 0; inode < nbFaceNodes; inode++) {
8961         poly_nodes.push_back(faceNodes[inode]);
8962
8963         if (nbInserted == 0) {
8964           if (faceNodes[inode] == theBetweenNode1) {
8965             if (faceNodes[inode + 1] == theBetweenNode2) {
8966               nbInserted = theNodesToInsert.size();
8967
8968               // add nodes to insert
8969               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8970               for (; nIt != theNodesToInsert.end(); nIt++) {
8971                 poly_nodes.push_back(*nIt);
8972               }
8973             }
8974           }
8975           else if (faceNodes[inode] == theBetweenNode2) {
8976             if (faceNodes[inode + 1] == theBetweenNode1) {
8977               nbInserted = theNodesToInsert.size();
8978
8979               // add nodes to insert in reversed order
8980               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8981               nIt--;
8982               for (; nIt != theNodesToInsert.begin(); nIt--) {
8983                 poly_nodes.push_back(*nIt);
8984               }
8985               poly_nodes.push_back(*nIt);
8986             }
8987           }
8988           else {
8989           }
8990         }
8991       }
8992       quantities[iface] = nbFaceNodes + nbInserted;
8993     }
8994
8995     // Replace the volume
8996     SMESHDS_Mesh *aMesh = GetMeshDS();
8997
8998     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8999     {
9000       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9001       myLastCreatedElems.Append( newElem );
9002       ReplaceElemInGroups( elem, newElem, aMesh );
9003     }
9004     aMesh->RemoveElement( elem );
9005   }
9006 }
9007
9008 namespace
9009 {
9010   //================================================================================
9011   /*!
9012    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9013    */
9014   //================================================================================
9015
9016   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9017                            vector<const SMDS_MeshNode *> & nodes,
9018                            vector<int> &                   nbNodeInFaces )
9019   {
9020     nodes.clear();
9021     nbNodeInFaces.clear();
9022     SMDS_VolumeTool vTool ( elem );
9023     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9024     {
9025       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9026       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9027       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9028     }
9029   }
9030 }
9031
9032 //=======================================================================
9033 /*!
9034  * \brief Convert elements contained in a sub-mesh to quadratic
9035  * \return int - nb of checked elements
9036  */
9037 //=======================================================================
9038
9039 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9040                                              SMESH_MesherHelper& theHelper,
9041                                              const bool          theForce3d)
9042 {
9043   int nbElem = 0;
9044   if( !theSm ) return nbElem;
9045
9046   vector<int> nbNodeInFaces;
9047   vector<const SMDS_MeshNode *> nodes;
9048   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9049   while(ElemItr->more())
9050   {
9051     nbElem++;
9052     const SMDS_MeshElement* elem = ElemItr->next();
9053     if( !elem ) continue;
9054
9055     // analyse a necessity of conversion
9056     const SMDSAbs_ElementType aType = elem->GetType();
9057     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9058       continue;
9059     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9060     bool hasCentralNodes = false;
9061     if ( elem->IsQuadratic() )
9062     {
9063       bool alreadyOK;
9064       switch ( aGeomType ) {
9065       case SMDSEntity_Quad_Triangle:
9066       case SMDSEntity_Quad_Quadrangle:
9067       case SMDSEntity_Quad_Hexa:
9068         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9069
9070       case SMDSEntity_BiQuad_Triangle:
9071       case SMDSEntity_BiQuad_Quadrangle:
9072       case SMDSEntity_TriQuad_Hexa:
9073         alreadyOK = theHelper.GetIsBiQuadratic();
9074         hasCentralNodes = true;
9075         break;
9076       default:
9077         alreadyOK = true;
9078       }
9079       // take into account already present modium nodes
9080       switch ( aType ) {
9081       case SMDSAbs_Volume:
9082         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9083       case SMDSAbs_Face:
9084         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9085       case SMDSAbs_Edge:
9086         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9087       default:;
9088       }
9089       if ( alreadyOK )
9090         continue;
9091     }
9092     // get elem data needed to re-create it
9093     //
9094     const int id      = elem->GetID();
9095     const int nbNodes = elem->NbCornerNodes();
9096     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9097     if ( aGeomType == SMDSEntity_Polyhedra )
9098       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9099     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9100       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9101
9102     // remove a linear element
9103     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9104
9105     // remove central nodes of biquadratic elements (biquad->quad convertion)
9106     if ( hasCentralNodes )
9107       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9108         if ( nodes[i]->NbInverseElements() == 0 )
9109           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9110
9111     const SMDS_MeshElement* NewElem = 0;
9112
9113     switch( aType )
9114     {
9115     case SMDSAbs_Edge :
9116       {
9117         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9118         break;
9119       }
9120     case SMDSAbs_Face :
9121       {
9122         switch(nbNodes)
9123         {
9124         case 3:
9125           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9126           break;
9127         case 4:
9128           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9129           break;
9130         default:
9131           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9132         }
9133         break;
9134       }
9135     case SMDSAbs_Volume :
9136       {
9137         switch( aGeomType )
9138         {
9139         case SMDSEntity_Tetra:
9140           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9141           break;
9142         case SMDSEntity_Pyramid:
9143           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9144           break;
9145         case SMDSEntity_Penta:
9146           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9147           break;
9148         case SMDSEntity_Hexa:
9149         case SMDSEntity_Quad_Hexa:
9150         case SMDSEntity_TriQuad_Hexa:
9151           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9152                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9153           break;
9154         case SMDSEntity_Hexagonal_Prism:
9155         default:
9156           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9157         }
9158         break;
9159       }
9160     default :
9161       continue;
9162     }
9163     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9164     if( NewElem && NewElem->getshapeId() < 1 )
9165       theSm->AddElement( NewElem );
9166   }
9167   return nbElem;
9168 }
9169 //=======================================================================
9170 //function : ConvertToQuadratic
9171 //purpose  :
9172 //=======================================================================
9173
9174 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9175 {
9176   SMESHDS_Mesh* meshDS = GetMeshDS();
9177
9178   SMESH_MesherHelper aHelper(*myMesh);
9179
9180   aHelper.SetIsQuadratic( true );
9181   aHelper.SetIsBiQuadratic( theToBiQuad );
9182   aHelper.SetElementsOnShape(true);
9183   aHelper.ToFixNodeParameters( true );
9184
9185   // convert elements assigned to sub-meshes
9186   int nbCheckedElems = 0;
9187   if ( myMesh->HasShapeToMesh() )
9188   {
9189     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9190     {
9191       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9192       while ( smIt->more() ) {
9193         SMESH_subMesh* sm = smIt->next();
9194         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9195           aHelper.SetSubShape( sm->GetSubShape() );
9196           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9197         }
9198       }
9199     }
9200   }
9201
9202   // convert elements NOT assigned to sub-meshes
9203   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9204   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9205   {
9206     aHelper.SetElementsOnShape(false);
9207     SMESHDS_SubMesh *smDS = 0;
9208
9209     // convert edges
9210     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9211     while( aEdgeItr->more() )
9212     {
9213       const SMDS_MeshEdge* edge = aEdgeItr->next();
9214       if ( !edge->IsQuadratic() )
9215       {
9216         int                  id = edge->GetID();
9217         const SMDS_MeshNode* n1 = edge->GetNode(0);
9218         const SMDS_MeshNode* n2 = edge->GetNode(1);
9219
9220         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9221
9222         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9223         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9224       }
9225       else
9226       {
9227         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9228       }
9229     }
9230
9231     // convert faces
9232     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9233     while( aFaceItr->more() )
9234     {
9235       const SMDS_MeshFace* face = aFaceItr->next();
9236       if ( !face ) continue;
9237       
9238       const SMDSAbs_EntityType type = face->GetEntityType();
9239       bool alreadyOK;
9240       switch( type )
9241       {
9242       case SMDSEntity_Quad_Triangle:
9243       case SMDSEntity_Quad_Quadrangle:
9244         alreadyOK = !theToBiQuad;
9245         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9246         break;
9247       case SMDSEntity_BiQuad_Triangle:
9248       case SMDSEntity_BiQuad_Quadrangle:
9249         alreadyOK = theToBiQuad;
9250         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9251         break;
9252       default: alreadyOK = false;
9253       }
9254       if ( alreadyOK )
9255         continue;
9256
9257       const int id = face->GetID();
9258       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9259
9260       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9261
9262       SMDS_MeshFace * NewFace = 0;
9263       switch( type )
9264       {
9265       case SMDSEntity_Triangle:
9266       case SMDSEntity_Quad_Triangle:
9267       case SMDSEntity_BiQuad_Triangle:
9268         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9269         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9270           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9271         break;
9272
9273       case SMDSEntity_Quadrangle:
9274       case SMDSEntity_Quad_Quadrangle:
9275       case SMDSEntity_BiQuad_Quadrangle:
9276         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9277         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9278           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9279         break;
9280
9281       default:;
9282         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9283       }
9284       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9285     }
9286
9287     // convert volumes
9288     vector<int> nbNodeInFaces;
9289     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9290     while(aVolumeItr->more())
9291     {
9292       const SMDS_MeshVolume* volume = aVolumeItr->next();
9293       if ( !volume ) continue;
9294
9295       const SMDSAbs_EntityType type = volume->GetEntityType();
9296       if ( volume->IsQuadratic() )
9297       {
9298         bool alreadyOK;
9299         switch ( type )
9300         {
9301         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9302         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9303         default:                      alreadyOK = true;
9304         }
9305         if ( alreadyOK )
9306         {
9307           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9308           continue;
9309         }
9310       }
9311       const int id = volume->GetID();
9312       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9313       if ( type == SMDSEntity_Polyhedra )
9314         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9315       else if ( type == SMDSEntity_Hexagonal_Prism )
9316         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9317
9318       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9319
9320       SMDS_MeshVolume * NewVolume = 0;
9321       switch ( type )
9322       {
9323       case SMDSEntity_Tetra:
9324         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9325         break;
9326       case SMDSEntity_Hexa:
9327       case SMDSEntity_Quad_Hexa:
9328       case SMDSEntity_TriQuad_Hexa:
9329         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9330                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9331         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9332           if ( nodes[i]->NbInverseElements() == 0 )
9333             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9334         break;
9335       case SMDSEntity_Pyramid:
9336         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9337                                       nodes[3], nodes[4], id, theForce3d);
9338         break;
9339       case SMDSEntity_Penta:
9340         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9341                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9342         break;
9343       case SMDSEntity_Hexagonal_Prism:
9344       default:
9345         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9346       }
9347       ReplaceElemInGroups(volume, NewVolume, meshDS);
9348     }
9349   }
9350
9351   if ( !theForce3d )
9352   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9353     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9354     // aHelper.FixQuadraticElements(myError);
9355     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9356   }
9357 }
9358
9359 //================================================================================
9360 /*!
9361  * \brief Makes given elements quadratic
9362  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9363  *  \param theElements - elements to make quadratic
9364  */
9365 //================================================================================
9366
9367 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9368                                           TIDSortedElemSet& theElements,
9369                                           const bool        theToBiQuad)
9370 {
9371   if ( theElements.empty() ) return;
9372
9373   // we believe that all theElements are of the same type
9374   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9375
9376   // get all nodes shared by theElements
9377   TIDSortedNodeSet allNodes;
9378   TIDSortedElemSet::iterator eIt = theElements.begin();
9379   for ( ; eIt != theElements.end(); ++eIt )
9380     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9381
9382   // complete theElements with elements of lower dim whose all nodes are in allNodes
9383
9384   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9385   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9386   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9387   for ( ; nIt != allNodes.end(); ++nIt )
9388   {
9389     const SMDS_MeshNode* n = *nIt;
9390     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9391     while ( invIt->more() )
9392     {
9393       const SMDS_MeshElement*      e = invIt->next();
9394       const SMDSAbs_ElementType type = e->GetType();
9395       if ( e->IsQuadratic() )
9396       {
9397         quadAdjacentElems[ type ].insert( e );
9398
9399         bool alreadyOK;
9400         switch ( e->GetEntityType() ) {
9401         case SMDSEntity_Quad_Triangle:
9402         case SMDSEntity_Quad_Quadrangle:
9403         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9404         case SMDSEntity_BiQuad_Triangle:
9405         case SMDSEntity_BiQuad_Quadrangle:
9406         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9407         default:                           alreadyOK = true;
9408         }
9409         if ( alreadyOK )
9410           continue;
9411       }
9412       if ( type >= elemType )
9413         continue; // same type or more complex linear element
9414
9415       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9416         continue; // e is already checked
9417
9418       // check nodes
9419       bool allIn = true;
9420       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9421       while ( nodeIt->more() && allIn )
9422         allIn = allNodes.count( nodeIt->next() );
9423       if ( allIn )
9424         theElements.insert(e );
9425     }
9426   }
9427
9428   SMESH_MesherHelper helper(*myMesh);
9429   helper.SetIsQuadratic( true );
9430   helper.SetIsBiQuadratic( theToBiQuad );
9431
9432   // add links of quadratic adjacent elements to the helper
9433
9434   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9435     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9436           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9437     {
9438       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9439     }
9440   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9441     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9442           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9443     {
9444       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9445     }
9446   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9447     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9448           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9449     {
9450       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9451     }
9452
9453   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9454
9455   SMESHDS_Mesh*  meshDS = GetMeshDS();
9456   SMESHDS_SubMesh* smDS = 0;
9457   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9458   {
9459     const SMDS_MeshElement* elem = *eIt;
9460
9461     bool alreadyOK;
9462     int nbCentralNodes = 0;
9463     switch ( elem->GetEntityType() ) {
9464       // linear convertible
9465     case SMDSEntity_Edge:
9466     case SMDSEntity_Triangle:
9467     case SMDSEntity_Quadrangle:
9468     case SMDSEntity_Tetra:
9469     case SMDSEntity_Pyramid:
9470     case SMDSEntity_Hexa:
9471     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9472       // quadratic that can become bi-quadratic
9473     case SMDSEntity_Quad_Triangle:
9474     case SMDSEntity_Quad_Quadrangle:
9475     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9476       // bi-quadratic
9477     case SMDSEntity_BiQuad_Triangle:
9478     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9479     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9480       // the rest
9481     default:                           alreadyOK = true;
9482     }
9483     if ( alreadyOK ) continue;
9484
9485     const SMDSAbs_ElementType type = elem->GetType();
9486     const int                   id = elem->GetID();
9487     const int              nbNodes = elem->NbCornerNodes();
9488     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9489
9490     helper.SetSubShape( elem->getshapeId() );
9491
9492     if ( !smDS || !smDS->Contains( elem ))
9493       smDS = meshDS->MeshElements( elem->getshapeId() );
9494     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9495
9496     SMDS_MeshElement * newElem = 0;
9497     switch( nbNodes )
9498     {
9499     case 4: // cases for most frequently used element types go first (for optimization)
9500       if ( type == SMDSAbs_Volume )
9501         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9502       else
9503         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9504       break;
9505     case 8:
9506       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9507                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9508       break;
9509     case 3:
9510       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9511       break;
9512     case 2:
9513       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9514       break;
9515     case 5:
9516       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9517                                  nodes[4], id, theForce3d);
9518       break;
9519     case 6:
9520       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9521                                  nodes[4], nodes[5], id, theForce3d);
9522       break;
9523     default:;
9524     }
9525     ReplaceElemInGroups( elem, newElem, meshDS);
9526     if( newElem && smDS )
9527       smDS->AddElement( newElem );
9528
9529      // remove central nodes
9530     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9531       if ( nodes[i]->NbInverseElements() == 0 )
9532         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9533
9534   } // loop on theElements
9535
9536   if ( !theForce3d )
9537   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9538     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9539     // helper.FixQuadraticElements( myError );
9540     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9541   }
9542 }
9543
9544 //=======================================================================
9545 /*!
9546  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9547  * \return int - nb of checked elements
9548  */
9549 //=======================================================================
9550
9551 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9552                                      SMDS_ElemIteratorPtr theItr,
9553                                      const int            theShapeID)
9554 {
9555   int nbElem = 0;
9556   SMESHDS_Mesh* meshDS = GetMeshDS();
9557   ElemFeatures elemType;
9558   vector<const SMDS_MeshNode *> nodes;
9559
9560   while( theItr->more() )
9561   {
9562     const SMDS_MeshElement* elem = theItr->next();
9563     nbElem++;
9564     if( elem && elem->IsQuadratic())
9565     {
9566       // get elem data
9567       int nbCornerNodes = elem->NbCornerNodes();
9568       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9569
9570       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9571
9572       //remove a quadratic element
9573       if ( !theSm || !theSm->Contains( elem ))
9574         theSm = meshDS->MeshElements( elem->getshapeId() );
9575       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9576
9577       // remove medium nodes
9578       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9579         if ( nodes[i]->NbInverseElements() == 0 )
9580           meshDS->RemoveFreeNode( nodes[i], theSm );
9581
9582       // add a linear element
9583       nodes.resize( nbCornerNodes );
9584       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9585       ReplaceElemInGroups(elem, newElem, meshDS);
9586       if( theSm && newElem )
9587         theSm->AddElement( newElem );
9588     }
9589   }
9590   return nbElem;
9591 }
9592
9593 //=======================================================================
9594 //function : ConvertFromQuadratic
9595 //purpose  :
9596 //=======================================================================
9597
9598 bool SMESH_MeshEditor::ConvertFromQuadratic()
9599 {
9600   int nbCheckedElems = 0;
9601   if ( myMesh->HasShapeToMesh() )
9602   {
9603     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9604     {
9605       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9606       while ( smIt->more() ) {
9607         SMESH_subMesh* sm = smIt->next();
9608         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9609           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9610       }
9611     }
9612   }
9613
9614   int totalNbElems =
9615     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9616   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9617   {
9618     SMESHDS_SubMesh *aSM = 0;
9619     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9620   }
9621
9622   return true;
9623 }
9624
9625 namespace
9626 {
9627   //================================================================================
9628   /*!
9629    * \brief Return true if all medium nodes of the element are in the node set
9630    */
9631   //================================================================================
9632
9633   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9634   {
9635     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9636       if ( !nodeSet.count( elem->GetNode(i) ))
9637         return false;
9638     return true;
9639   }
9640 }
9641
9642 //================================================================================
9643 /*!
9644  * \brief Makes given elements linear
9645  */
9646 //================================================================================
9647
9648 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9649 {
9650   if ( theElements.empty() ) return;
9651
9652   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9653   set<int> mediumNodeIDs;
9654   TIDSortedElemSet::iterator eIt = theElements.begin();
9655   for ( ; eIt != theElements.end(); ++eIt )
9656   {
9657     const SMDS_MeshElement* e = *eIt;
9658     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9659       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9660   }
9661
9662   // replace given elements by linear ones
9663   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9664   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9665
9666   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9667   // except those elements sharing medium nodes of quadratic element whose medium nodes
9668   // are not all in mediumNodeIDs
9669
9670   // get remaining medium nodes
9671   TIDSortedNodeSet mediumNodes;
9672   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9673   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9674     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9675       mediumNodes.insert( mediumNodes.end(), n );
9676
9677   // find more quadratic elements to convert
9678   TIDSortedElemSet moreElemsToConvert;
9679   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9680   for ( ; nIt != mediumNodes.end(); ++nIt )
9681   {
9682     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9683     while ( invIt->more() )
9684     {
9685       const SMDS_MeshElement* e = invIt->next();
9686       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9687       {
9688         // find a more complex element including e and
9689         // whose medium nodes are not in mediumNodes
9690         bool complexFound = false;
9691         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9692         {
9693           SMDS_ElemIteratorPtr invIt2 =
9694             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9695           while ( invIt2->more() )
9696           {
9697             const SMDS_MeshElement* eComplex = invIt2->next();
9698             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9699             {
9700               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9701               if ( nbCommonNodes == e->NbNodes())
9702               {
9703                 complexFound = true;
9704                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9705                 break;
9706               }
9707             }
9708           }
9709         }
9710         if ( !complexFound )
9711           moreElemsToConvert.insert( e );
9712       }
9713     }
9714   }
9715   elemIt = elemSetIterator( moreElemsToConvert );
9716   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9717 }
9718
9719 //=======================================================================
9720 //function : SewSideElements
9721 //purpose  :
9722 //=======================================================================
9723
9724 SMESH_MeshEditor::Sew_Error
9725 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9726                                    TIDSortedElemSet&    theSide2,
9727                                    const SMDS_MeshNode* theFirstNode1,
9728                                    const SMDS_MeshNode* theFirstNode2,
9729                                    const SMDS_MeshNode* theSecondNode1,
9730                                    const SMDS_MeshNode* theSecondNode2)
9731 {
9732   myLastCreatedElems.Clear();
9733   myLastCreatedNodes.Clear();
9734
9735   if ( theSide1.size() != theSide2.size() )
9736     return SEW_DIFF_NB_OF_ELEMENTS;
9737
9738   Sew_Error aResult = SEW_OK;
9739   // Algo:
9740   // 1. Build set of faces representing each side
9741   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9742   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9743
9744   // =======================================================================
9745   // 1. Build set of faces representing each side:
9746   // =======================================================================
9747   // a. build set of nodes belonging to faces
9748   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9749   // c. create temporary faces representing side of volumes if correspondent
9750   //    face does not exist
9751
9752   SMESHDS_Mesh* aMesh = GetMeshDS();
9753   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9754   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9755   TIDSortedElemSet             faceSet1, faceSet2;
9756   set<const SMDS_MeshElement*> volSet1,  volSet2;
9757   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9758   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9759   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9760   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9761   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9762   int iSide, iFace, iNode;
9763
9764   list<const SMDS_MeshElement* > tempFaceList;
9765   for ( iSide = 0; iSide < 2; iSide++ ) {
9766     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9767     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9768     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9769     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9770     set<const SMDS_MeshElement*>::iterator vIt;
9771     TIDSortedElemSet::iterator eIt;
9772     set<const SMDS_MeshNode*>::iterator    nIt;
9773
9774     // check that given nodes belong to given elements
9775     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9776     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9777     int firstIndex = -1, secondIndex = -1;
9778     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9779       const SMDS_MeshElement* elem = *eIt;
9780       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9781       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9782       if ( firstIndex > -1 && secondIndex > -1 ) break;
9783     }
9784     if ( firstIndex < 0 || secondIndex < 0 ) {
9785       // we can simply return until temporary faces created
9786       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9787     }
9788
9789     // -----------------------------------------------------------
9790     // 1a. Collect nodes of existing faces
9791     //     and build set of face nodes in order to detect missing
9792     //     faces corresponding to sides of volumes
9793     // -----------------------------------------------------------
9794
9795     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9796
9797     // loop on the given element of a side
9798     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9799       //const SMDS_MeshElement* elem = *eIt;
9800       const SMDS_MeshElement* elem = *eIt;
9801       if ( elem->GetType() == SMDSAbs_Face ) {
9802         faceSet->insert( elem );
9803         set <const SMDS_MeshNode*> faceNodeSet;
9804         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9805         while ( nodeIt->more() ) {
9806           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9807           nodeSet->insert( n );
9808           faceNodeSet.insert( n );
9809         }
9810         setOfFaceNodeSet.insert( faceNodeSet );
9811       }
9812       else if ( elem->GetType() == SMDSAbs_Volume )
9813         volSet->insert( elem );
9814     }
9815     // ------------------------------------------------------------------------------
9816     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9817     // ------------------------------------------------------------------------------
9818
9819     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9820       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9821       while ( fIt->more() ) { // loop on faces sharing a node
9822         const SMDS_MeshElement* f = fIt->next();
9823         if ( faceSet->find( f ) == faceSet->end() ) {
9824           // check if all nodes are in nodeSet and
9825           // complete setOfFaceNodeSet if they are
9826           set <const SMDS_MeshNode*> faceNodeSet;
9827           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9828           bool allInSet = true;
9829           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9830             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9831             if ( nodeSet->find( n ) == nodeSet->end() )
9832               allInSet = false;
9833             else
9834               faceNodeSet.insert( n );
9835           }
9836           if ( allInSet ) {
9837             faceSet->insert( f );
9838             setOfFaceNodeSet.insert( faceNodeSet );
9839           }
9840         }
9841       }
9842     }
9843
9844     // -------------------------------------------------------------------------
9845     // 1c. Create temporary faces representing sides of volumes if correspondent
9846     //     face does not exist
9847     // -------------------------------------------------------------------------
9848
9849     if ( !volSet->empty() ) {
9850       //int nodeSetSize = nodeSet->size();
9851
9852       // loop on given volumes
9853       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9854         SMDS_VolumeTool vol (*vIt);
9855         // loop on volume faces: find free faces
9856         // --------------------------------------
9857         list<const SMDS_MeshElement* > freeFaceList;
9858         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9859           if ( !vol.IsFreeFace( iFace ))
9860             continue;
9861           // check if there is already a face with same nodes in a face set
9862           const SMDS_MeshElement* aFreeFace = 0;
9863           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9864           int nbNodes = vol.NbFaceNodes( iFace );
9865           set <const SMDS_MeshNode*> faceNodeSet;
9866           vol.GetFaceNodes( iFace, faceNodeSet );
9867           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9868           if ( isNewFace ) {
9869             // no such a face is given but it still can exist, check it
9870             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9871             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9872           }
9873           if ( !aFreeFace ) {
9874             // create a temporary face
9875             if ( nbNodes == 3 ) {
9876               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9877               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9878             }
9879             else if ( nbNodes == 4 ) {
9880               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9881               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9882             }
9883             else {
9884               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9885               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9886               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9887             }
9888             if ( aFreeFace )
9889               tempFaceList.push_back( aFreeFace );
9890           }
9891
9892           if ( aFreeFace )
9893             freeFaceList.push_back( aFreeFace );
9894
9895         } // loop on faces of a volume
9896
9897         // choose one of several free faces of a volume
9898         // --------------------------------------------
9899         if ( freeFaceList.size() > 1 ) {
9900           // choose a face having max nb of nodes shared by other elems of a side
9901           int maxNbNodes = -1;
9902           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9903           while ( fIt != freeFaceList.end() ) { // loop on free faces
9904             int nbSharedNodes = 0;
9905             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9906             while ( nodeIt->more() ) { // loop on free face nodes
9907               const SMDS_MeshNode* n =
9908                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9910               while ( invElemIt->more() ) {
9911                 const SMDS_MeshElement* e = invElemIt->next();
9912                 nbSharedNodes += faceSet->count( e );
9913                 nbSharedNodes += elemSet->count( e );
9914               }
9915             }
9916             if ( nbSharedNodes > maxNbNodes ) {
9917               maxNbNodes = nbSharedNodes;
9918               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9919             }
9920             else if ( nbSharedNodes == maxNbNodes ) {
9921               fIt++;
9922             }
9923             else {
9924               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9925             }
9926           }
9927           if ( freeFaceList.size() > 1 )
9928           {
9929             // could not choose one face, use another way
9930             // choose a face most close to the bary center of the opposite side
9931             gp_XYZ aBC( 0., 0., 0. );
9932             set <const SMDS_MeshNode*> addedNodes;
9933             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9934             eIt = elemSet2->begin();
9935             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9936               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9937               while ( nodeIt->more() ) { // loop on free face nodes
9938                 const SMDS_MeshNode* n =
9939                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9940                 if ( addedNodes.insert( n ).second )
9941                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9942               }
9943             }
9944             aBC /= addedNodes.size();
9945             double minDist = DBL_MAX;
9946             fIt = freeFaceList.begin();
9947             while ( fIt != freeFaceList.end() ) { // loop on free faces
9948               double dist = 0;
9949               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9950               while ( nodeIt->more() ) { // loop on free face nodes
9951                 const SMDS_MeshNode* n =
9952                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9953                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9954                 dist += ( aBC - p ).SquareModulus();
9955               }
9956               if ( dist < minDist ) {
9957                 minDist = dist;
9958                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9959               }
9960               else
9961                 fIt = freeFaceList.erase( fIt++ );
9962             }
9963           }
9964         } // choose one of several free faces of a volume
9965
9966         if ( freeFaceList.size() == 1 ) {
9967           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9968           faceSet->insert( aFreeFace );
9969           // complete a node set with nodes of a found free face
9970           //           for ( iNode = 0; iNode < ; iNode++ )
9971           //             nodeSet->insert( fNodes[ iNode ] );
9972         }
9973
9974       } // loop on volumes of a side
9975
9976       //       // complete a set of faces if new nodes in a nodeSet appeared
9977       //       // ----------------------------------------------------------
9978       //       if ( nodeSetSize != nodeSet->size() ) {
9979       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9980       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9981       //           while ( fIt->more() ) { // loop on faces sharing a node
9982       //             const SMDS_MeshElement* f = fIt->next();
9983       //             if ( faceSet->find( f ) == faceSet->end() ) {
9984       //               // check if all nodes are in nodeSet and
9985       //               // complete setOfFaceNodeSet if they are
9986       //               set <const SMDS_MeshNode*> faceNodeSet;
9987       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9988       //               bool allInSet = true;
9989       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9990       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9991       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9992       //                   allInSet = false;
9993       //                 else
9994       //                   faceNodeSet.insert( n );
9995       //               }
9996       //               if ( allInSet ) {
9997       //                 faceSet->insert( f );
9998       //                 setOfFaceNodeSet.insert( faceNodeSet );
9999       //               }
10000       //             }
10001       //           }
10002       //         }
10003       //       }
10004     } // Create temporary faces, if there are volumes given
10005   } // loop on sides
10006
10007   if ( faceSet1.size() != faceSet2.size() ) {
10008     // delete temporary faces: they are in reverseElements of actual nodes
10009 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10010 //    while ( tmpFaceIt->more() )
10011 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10012 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10013 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10014 //      aMesh->RemoveElement(*tmpFaceIt);
10015     MESSAGE("Diff nb of faces");
10016     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10017   }
10018
10019   // ============================================================
10020   // 2. Find nodes to merge:
10021   //              bind a node to remove to a node to put instead
10022   // ============================================================
10023
10024   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10025   if ( theFirstNode1 != theFirstNode2 )
10026     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10027   if ( theSecondNode1 != theSecondNode2 )
10028     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10029
10030   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10031   set< long > linkIdSet; // links to process
10032   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10033
10034   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10035   list< NLink > linkList[2];
10036   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10037   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10038   // loop on links in linkList; find faces by links and append links
10039   // of the found faces to linkList
10040   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10041   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10042   {
10043     NLink link[] = { *linkIt[0], *linkIt[1] };
10044     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10045     if ( !linkIdSet.count( linkID ) )
10046       continue;
10047
10048     // by links, find faces in the face sets,
10049     // and find indices of link nodes in the found faces;
10050     // in a face set, there is only one or no face sharing a link
10051     // ---------------------------------------------------------------
10052
10053     const SMDS_MeshElement* face[] = { 0, 0 };
10054     vector<const SMDS_MeshNode*> fnodes[2];
10055     int iLinkNode[2][2];
10056     TIDSortedElemSet avoidSet;
10057     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10058       const SMDS_MeshNode* n1 = link[iSide].first;
10059       const SMDS_MeshNode* n2 = link[iSide].second;
10060       //cout << "Side " << iSide << " ";
10061       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10062       // find a face by two link nodes
10063       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10064                                                       *faceSetPtr[ iSide ], avoidSet,
10065                                                       &iLinkNode[iSide][0],
10066                                                       &iLinkNode[iSide][1] );
10067       if ( face[ iSide ])
10068       {
10069         //cout << " F " << face[ iSide]->GetID() <<endl;
10070         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10071         // put face nodes to fnodes
10072         if ( face[ iSide ]->IsQuadratic() )
10073         {
10074           // use interlaced nodes iterator
10075           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10076           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10077           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10078           while ( nIter->more() )
10079             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10080         }
10081         else
10082         {
10083           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10084                                   face[ iSide ]->end_nodes() );
10085         }
10086         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10087       }
10088     }
10089
10090     // check similarity of elements of the sides
10091     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10092       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10093       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10094         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10095       }
10096       else {
10097         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10098       }
10099       break; // do not return because it's necessary to remove tmp faces
10100     }
10101
10102     // set nodes to merge
10103     // -------------------
10104
10105     if ( face[0] && face[1] )  {
10106       const int nbNodes = face[0]->NbNodes();
10107       if ( nbNodes != face[1]->NbNodes() ) {
10108         MESSAGE("Diff nb of face nodes");
10109         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10110         break; // do not return because it s necessary to remove tmp faces
10111       }
10112       bool reverse[] = { false, false }; // order of nodes in the link
10113       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10114         // analyse link orientation in faces
10115         int i1 = iLinkNode[ iSide ][ 0 ];
10116         int i2 = iLinkNode[ iSide ][ 1 ];
10117         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10118       }
10119       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10120       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10121       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10122       {
10123         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10124                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10125       }
10126
10127       // add other links of the faces to linkList
10128       // -----------------------------------------
10129
10130       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10131         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10132         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10133         if ( !iter_isnew.second ) { // already in a set: no need to process
10134           linkIdSet.erase( iter_isnew.first );
10135         }
10136         else // new in set == encountered for the first time: add
10137         {
10138           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10139           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10140           linkList[0].push_back ( NLink( n1, n2 ));
10141           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10142         }
10143       }
10144     } // 2 faces found
10145
10146     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10147       break;
10148
10149   } // loop on link lists
10150
10151   if ( aResult == SEW_OK &&
10152        ( //linkIt[0] != linkList[0].end() ||
10153          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10154     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10155              " " << (faceSetPtr[1]->empty()));
10156     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10157   }
10158
10159   // ====================================================================
10160   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10161   // ====================================================================
10162
10163   // delete temporary faces
10164 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10165 //  while ( tmpFaceIt->more() )
10166 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10167   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10168   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10169     aMesh->RemoveElement(*tmpFaceIt);
10170
10171   if ( aResult != SEW_OK)
10172     return aResult;
10173
10174   list< int > nodeIDsToRemove;
10175   vector< const SMDS_MeshNode*> nodes;
10176   ElemFeatures elemType;
10177
10178   // loop on nodes replacement map
10179   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10180   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10181     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10182     {
10183       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10184       nodeIDsToRemove.push_back( nToRemove->GetID() );
10185       // loop on elements sharing nToRemove
10186       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10187       while ( invElemIt->more() ) {
10188         const SMDS_MeshElement* e = invElemIt->next();
10189         // get a new suite of nodes: make replacement
10190         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10191         nodes.resize( nbNodes );
10192         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10193         while ( nIt->more() ) {
10194           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10195           nnIt = nReplaceMap.find( n );
10196           if ( nnIt != nReplaceMap.end() ) {
10197             nbReplaced++;
10198             n = (*nnIt).second;
10199           }
10200           nodes[ i++ ] = n;
10201         }
10202         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10203         //         elemIDsToRemove.push_back( e->GetID() );
10204         //       else
10205         if ( nbReplaced )
10206         {
10207           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10208           aMesh->RemoveElement( e );
10209
10210           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10211           {
10212             AddToSameGroups( newElem, e, aMesh );
10213             if ( int aShapeId = e->getshapeId() )
10214               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10215           }
10216         }
10217       }
10218     }
10219
10220   Remove( nodeIDsToRemove, true );
10221
10222   return aResult;
10223 }
10224
10225 //================================================================================
10226 /*!
10227  * \brief Find corresponding nodes in two sets of faces
10228  * \param theSide1 - first face set
10229  * \param theSide2 - second first face
10230  * \param theFirstNode1 - a boundary node of set 1
10231  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10232  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10233  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10234  * \param nReplaceMap - output map of corresponding nodes
10235  * \return bool  - is a success or not
10236  */
10237 //================================================================================
10238
10239 #ifdef _DEBUG_
10240 //#define DEBUG_MATCHING_NODES
10241 #endif
10242
10243 SMESH_MeshEditor::Sew_Error
10244 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10245                                     set<const SMDS_MeshElement*>& theSide2,
10246                                     const SMDS_MeshNode*          theFirstNode1,
10247                                     const SMDS_MeshNode*          theFirstNode2,
10248                                     const SMDS_MeshNode*          theSecondNode1,
10249                                     const SMDS_MeshNode*          theSecondNode2,
10250                                     TNodeNodeMap &                nReplaceMap)
10251 {
10252   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10253
10254   nReplaceMap.clear();
10255   if ( theFirstNode1 != theFirstNode2 )
10256     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10257   if ( theSecondNode1 != theSecondNode2 )
10258     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10259
10260   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10261   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10262
10263   list< NLink > linkList[2];
10264   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10265   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10266
10267   // loop on links in linkList; find faces by links and append links
10268   // of the found faces to linkList
10269   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10270   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10271     NLink link[] = { *linkIt[0], *linkIt[1] };
10272     if ( linkSet.find( link[0] ) == linkSet.end() )
10273       continue;
10274
10275     // by links, find faces in the face sets,
10276     // and find indices of link nodes in the found faces;
10277     // in a face set, there is only one or no face sharing a link
10278     // ---------------------------------------------------------------
10279
10280     const SMDS_MeshElement* face[] = { 0, 0 };
10281     list<const SMDS_MeshNode*> notLinkNodes[2];
10282     //bool reverse[] = { false, false }; // order of notLinkNodes
10283     int nbNodes[2];
10284     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10285     {
10286       const SMDS_MeshNode* n1 = link[iSide].first;
10287       const SMDS_MeshNode* n2 = link[iSide].second;
10288       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10289       set< const SMDS_MeshElement* > facesOfNode1;
10290       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10291       {
10292         // during a loop of the first node, we find all faces around n1,
10293         // during a loop of the second node, we find one face sharing both n1 and n2
10294         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10295         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10296         while ( fIt->more() ) { // loop on faces sharing a node
10297           const SMDS_MeshElement* f = fIt->next();
10298           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10299               ! facesOfNode1.insert( f ).second ) // f encounters twice
10300           {
10301             if ( face[ iSide ] ) {
10302               MESSAGE( "2 faces per link " );
10303               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10304             }
10305             face[ iSide ] = f;
10306             faceSet->erase( f );
10307
10308             // get not link nodes
10309             int nbN = f->NbNodes();
10310             if ( f->IsQuadratic() )
10311               nbN /= 2;
10312             nbNodes[ iSide ] = nbN;
10313             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10314             int i1 = f->GetNodeIndex( n1 );
10315             int i2 = f->GetNodeIndex( n2 );
10316             int iEnd = nbN, iBeg = -1, iDelta = 1;
10317             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10318             if ( reverse ) {
10319               std::swap( iEnd, iBeg ); iDelta = -1;
10320             }
10321             int i = i2;
10322             while ( true ) {
10323               i += iDelta;
10324               if ( i == iEnd ) i = iBeg + iDelta;
10325               if ( i == i1 ) break;
10326               nodes.push_back ( f->GetNode( i ) );
10327             }
10328           }
10329         }
10330       }
10331     }
10332     // check similarity of elements of the sides
10333     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10334       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10335       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10336         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10337       }
10338       else {
10339         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10340       }
10341     }
10342
10343     // set nodes to merge
10344     // -------------------
10345
10346     if ( face[0] && face[1] )  {
10347       if ( nbNodes[0] != nbNodes[1] ) {
10348         MESSAGE("Diff nb of face nodes");
10349         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10350       }
10351 #ifdef DEBUG_MATCHING_NODES
10352       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10353                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10354                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10355 #endif
10356       int nbN = nbNodes[0];
10357       {
10358         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10359         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10360         for ( int i = 0 ; i < nbN - 2; ++i ) {
10361 #ifdef DEBUG_MATCHING_NODES
10362           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10363 #endif
10364           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10365         }
10366       }
10367
10368       // add other links of the face 1 to linkList
10369       // -----------------------------------------
10370
10371       const SMDS_MeshElement* f0 = face[0];
10372       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10373       for ( int i = 0; i < nbN; i++ )
10374       {
10375         const SMDS_MeshNode* n2 = f0->GetNode( i );
10376         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10377           linkSet.insert( SMESH_TLink( n1, n2 ));
10378         if ( !iter_isnew.second ) { // already in a set: no need to process
10379           linkSet.erase( iter_isnew.first );
10380         }
10381         else // new in set == encountered for the first time: add
10382         {
10383 #ifdef DEBUG_MATCHING_NODES
10384           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10385                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10386 #endif
10387           linkList[0].push_back ( NLink( n1, n2 ));
10388           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10389         }
10390         n1 = n2;
10391       }
10392     } // 2 faces found
10393   } // loop on link lists
10394
10395   return SEW_OK;
10396 }
10397
10398 //================================================================================
10399 /*!
10400  * \brief Create elements equal (on same nodes) to given ones
10401  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10402  *              elements of the uppest dimension are duplicated.
10403  */
10404 //================================================================================
10405
10406 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10407 {
10408   ClearLastCreated();
10409   SMESHDS_Mesh* mesh = GetMeshDS();
10410
10411   // get an element type and an iterator over elements
10412
10413   SMDSAbs_ElementType type = SMDSAbs_All;
10414   SMDS_ElemIteratorPtr elemIt;
10415   vector< const SMDS_MeshElement* > allElems;
10416   if ( theElements.empty() )
10417   {
10418     if ( mesh->NbNodes() == 0 )
10419       return;
10420     // get most complex type
10421     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10422       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10423       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10424     };
10425     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10426       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10427       {
10428         type = types[i];
10429         break;
10430       }
10431     // put all elements in the vector <allElems>
10432     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10433     elemIt = mesh->elementsIterator( type );
10434     while ( elemIt->more() )
10435       allElems.push_back( elemIt->next());
10436     elemIt = elemSetIterator( allElems );
10437   }
10438   else
10439   {
10440     type = (*theElements.begin())->GetType();
10441     elemIt = elemSetIterator( theElements );
10442   }
10443
10444   // duplicate elements
10445
10446   ElemFeatures elemType;
10447
10448   vector< const SMDS_MeshNode* > nodes;
10449   while ( elemIt->more() )
10450   {
10451     const SMDS_MeshElement* elem = elemIt->next();
10452     if ( elem->GetType() != type )
10453       continue;
10454
10455     elemType.Init( elem, /*basicOnly=*/false );
10456     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10457
10458     AddElement( nodes, elemType );
10459   }
10460 }
10461
10462 //================================================================================
10463 /*!
10464   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10465   \param theElems - the list of elements (edges or faces) to be replicated
10466   The nodes for duplication could be found from these elements
10467   \param theNodesNot - list of nodes to NOT replicate
10468   \param theAffectedElems - the list of elements (cells and edges) to which the
10469   replicated nodes should be associated to.
10470   \return TRUE if operation has been completed successfully, FALSE otherwise
10471 */
10472 //================================================================================
10473
10474 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10475                                     const TIDSortedElemSet& theNodesNot,
10476                                     const TIDSortedElemSet& theAffectedElems )
10477 {
10478   myLastCreatedElems.Clear();
10479   myLastCreatedNodes.Clear();
10480
10481   if ( theElems.size() == 0 )
10482     return false;
10483
10484   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10485   if ( !aMeshDS )
10486     return false;
10487
10488   bool res = false;
10489   TNodeNodeMap anOldNodeToNewNode;
10490   // duplicate elements and nodes
10491   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10492   // replce nodes by duplications
10493   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10494   return res;
10495 }
10496
10497 //================================================================================
10498 /*!
10499   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10500   \param theMeshDS - mesh instance
10501   \param theElems - the elements replicated or modified (nodes should be changed)
10502   \param theNodesNot - nodes to NOT replicate
10503   \param theNodeNodeMap - relation of old node to new created node
10504   \param theIsDoubleElem - flag os to replicate element or modify
10505   \return TRUE if operation has been completed successfully, FALSE otherwise
10506 */
10507 //================================================================================
10508
10509 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10510                                    const TIDSortedElemSet& theElems,
10511                                    const TIDSortedElemSet& theNodesNot,
10512                                    TNodeNodeMap&           theNodeNodeMap,
10513                                    const bool              theIsDoubleElem )
10514 {
10515   // iterate through element and duplicate them (by nodes duplication)
10516   bool res = false;
10517   std::vector<const SMDS_MeshNode*> newNodes;
10518   ElemFeatures elemType;
10519
10520   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10521   for ( ;  elemItr != theElems.end(); ++elemItr )
10522   {
10523     const SMDS_MeshElement* anElem = *elemItr;
10524     if (!anElem)
10525       continue;
10526
10527     // duplicate nodes to duplicate element
10528     bool isDuplicate = false;
10529     newNodes.resize( anElem->NbNodes() );
10530     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10531     int ind = 0;
10532     while ( anIter->more() )
10533     {
10534       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10535       const SMDS_MeshNode*  aNewNode = aCurrNode;
10536       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10537       if ( n2n != theNodeNodeMap.end() )
10538       {
10539         aNewNode = n2n->second;
10540       }
10541       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10542       {
10543         // duplicate node
10544         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10545         copyPosition( aCurrNode, aNewNode );
10546         theNodeNodeMap[ aCurrNode ] = aNewNode;
10547         myLastCreatedNodes.Append( aNewNode );
10548       }
10549       isDuplicate |= (aCurrNode != aNewNode);
10550       newNodes[ ind++ ] = aNewNode;
10551     }
10552     if ( !isDuplicate )
10553       continue;
10554
10555     if ( theIsDoubleElem )
10556       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10557     else
10558       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10559
10560     res = true;
10561   }
10562   return res;
10563 }
10564
10565 //================================================================================
10566 /*!
10567   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10568   \param theNodes - identifiers of nodes to be doubled
10569   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10570   nodes. If list of element identifiers is empty then nodes are doubled but
10571   they not assigned to elements
10572   \return TRUE if operation has been completed successfully, FALSE otherwise
10573 */
10574 //================================================================================
10575
10576 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10577                                     const std::list< int >& theListOfModifiedElems )
10578 {
10579   myLastCreatedElems.Clear();
10580   myLastCreatedNodes.Clear();
10581
10582   if ( theListOfNodes.size() == 0 )
10583     return false;
10584
10585   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10586   if ( !aMeshDS )
10587     return false;
10588
10589   // iterate through nodes and duplicate them
10590
10591   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10592
10593   std::list< int >::const_iterator aNodeIter;
10594   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10595   {
10596     int aCurr = *aNodeIter;
10597     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10598     if ( !aNode )
10599       continue;
10600
10601     // duplicate node
10602
10603     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10604     if ( aNewNode )
10605     {
10606       copyPosition( aNode, aNewNode );
10607       anOldNodeToNewNode[ aNode ] = aNewNode;
10608       myLastCreatedNodes.Append( aNewNode );
10609     }
10610   }
10611
10612   // Create map of new nodes for modified elements
10613
10614   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10615
10616   std::list< int >::const_iterator anElemIter;
10617   for ( anElemIter = theListOfModifiedElems.begin();
10618         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10619   {
10620     int aCurr = *anElemIter;
10621     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10622     if ( !anElem )
10623       continue;
10624
10625     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10626
10627     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10628     int ind = 0;
10629     while ( anIter->more() )
10630     {
10631       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10632       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10633       {
10634         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10635         aNodeArr[ ind++ ] = aNewNode;
10636       }
10637       else
10638         aNodeArr[ ind++ ] = aCurrNode;
10639     }
10640     anElemToNodes[ anElem ] = aNodeArr;
10641   }
10642
10643   // Change nodes of elements
10644
10645   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10646     anElemToNodesIter = anElemToNodes.begin();
10647   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10648   {
10649     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10650     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10651     if ( anElem )
10652     {
10653       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10654     }
10655   }
10656
10657   return true;
10658 }
10659
10660 namespace {
10661
10662   //================================================================================
10663   /*!
10664   \brief Check if element located inside shape
10665   \return TRUE if IN or ON shape, FALSE otherwise
10666   */
10667   //================================================================================
10668
10669   template<class Classifier>
10670   bool isInside(const SMDS_MeshElement* theElem,
10671                 Classifier&             theClassifier,
10672                 const double            theTol)
10673   {
10674     gp_XYZ centerXYZ (0, 0, 0);
10675     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10676     while (aNodeItr->more())
10677       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10678
10679     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10680     theClassifier.Perform(aPnt, theTol);
10681     TopAbs_State aState = theClassifier.State();
10682     return (aState == TopAbs_IN || aState == TopAbs_ON );
10683   }
10684
10685   //================================================================================
10686   /*!
10687    * \brief Classifier of the 3D point on the TopoDS_Face
10688    *        with interaface suitable for isInside()
10689    */
10690   //================================================================================
10691
10692   struct _FaceClassifier
10693   {
10694     Extrema_ExtPS       _extremum;
10695     BRepAdaptor_Surface _surface;
10696     TopAbs_State        _state;
10697
10698     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10699     {
10700       _extremum.Initialize( _surface,
10701                             _surface.FirstUParameter(), _surface.LastUParameter(),
10702                             _surface.FirstVParameter(), _surface.LastVParameter(),
10703                             _surface.Tolerance(), _surface.Tolerance() );
10704     }
10705     void Perform(const gp_Pnt& aPnt, double theTol)
10706     {
10707       theTol *= theTol;
10708       _state = TopAbs_OUT;
10709       _extremum.Perform(aPnt);
10710       if ( _extremum.IsDone() )
10711         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10712           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10713     }
10714     TopAbs_State State() const
10715     {
10716       return _state;
10717     }
10718   };
10719 }
10720
10721 //================================================================================
10722 /*!
10723   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10724   This method is the first step of DoubleNodeElemGroupsInRegion.
10725   \param theElems - list of groups of elements (edges or faces) to be replicated
10726   \param theNodesNot - list of groups of nodes not to replicated
10727   \param theShape - shape to detect affected elements (element which geometric center
10728          located on or inside shape). If the shape is null, detection is done on faces orientations
10729          (select elements with a gravity center on the side given by faces normals).
10730          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10731          The replicated nodes should be associated to affected elements.
10732   \return groups of affected elements
10733   \sa DoubleNodeElemGroupsInRegion()
10734  */
10735 //================================================================================
10736
10737 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10738                                                    const TIDSortedElemSet& theNodesNot,
10739                                                    const TopoDS_Shape&     theShape,
10740                                                    TIDSortedElemSet&       theAffectedElems)
10741 {
10742   if ( theShape.IsNull() )
10743   {
10744     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10745     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10746     std::set<const SMDS_MeshElement*> edgesToCheck;
10747     alreadyCheckedNodes.clear();
10748     alreadyCheckedElems.clear();
10749     edgesToCheck.clear();
10750
10751     // --- iterates on elements to be replicated and get elements by back references from their nodes
10752
10753     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10754     int ielem;
10755     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10756     {
10757       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10758       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10759         continue;
10760       gp_XYZ normal;
10761       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10762       std::set<const SMDS_MeshNode*> nodesElem;
10763       nodesElem.clear();
10764       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10765       while ( nodeItr->more() )
10766       {
10767         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10768         nodesElem.insert(aNode);
10769       }
10770       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10771       for (; nodit != nodesElem.end(); nodit++)
10772       {
10773         const SMDS_MeshNode* aNode = *nodit;
10774         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10775           continue;
10776         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10777           continue;
10778         alreadyCheckedNodes.insert(aNode);
10779         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10780         while ( backElemItr->more() )
10781         {
10782           const SMDS_MeshElement* curElem = backElemItr->next();
10783           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10784             continue;
10785           if (theElems.find(curElem) != theElems.end())
10786             continue;
10787           alreadyCheckedElems.insert(curElem);
10788           double x=0, y=0, z=0;
10789           int nb = 0;
10790           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10791           while ( nodeItr2->more() )
10792           {
10793             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10794             x += anotherNode->X();
10795             y += anotherNode->Y();
10796             z += anotherNode->Z();
10797             nb++;
10798           }
10799           gp_XYZ p;
10800           p.SetCoord( x/nb -aNode->X(),
10801                       y/nb -aNode->Y(),
10802                       z/nb -aNode->Z() );
10803           if (normal*p > 0)
10804           {
10805             theAffectedElems.insert( curElem );
10806           }
10807           else if (curElem->GetType() == SMDSAbs_Edge)
10808             edgesToCheck.insert(curElem);
10809         }
10810       }
10811     }
10812     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10813     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10814     for( ; eit != edgesToCheck.end(); eit++)
10815     {
10816       bool onside = true;
10817       const SMDS_MeshElement* anEdge = *eit;
10818       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10819       while ( nodeItr->more() )
10820       {
10821         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10822         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10823         {
10824           onside = false;
10825           break;
10826         }
10827       }
10828       if (onside)
10829       {
10830         theAffectedElems.insert(anEdge);
10831       }
10832     }
10833   }
10834   else
10835   {
10836     const double aTol = Precision::Confusion();
10837     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10838     auto_ptr<_FaceClassifier>              aFaceClassifier;
10839     if ( theShape.ShapeType() == TopAbs_SOLID )
10840     {
10841       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10842       bsc3d->PerformInfinitePoint(aTol);
10843     }
10844     else if (theShape.ShapeType() == TopAbs_FACE )
10845     {
10846       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10847     }
10848
10849     // iterates on indicated elements and get elements by back references from their nodes
10850     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10851     int ielem;
10852     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10853     {
10854       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10855       if (!anElem)
10856         continue;
10857       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10858       while ( nodeItr->more() )
10859       {
10860         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10861         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10862           continue;
10863         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10864         while ( backElemItr->more() )
10865         {
10866           const SMDS_MeshElement* curElem = backElemItr->next();
10867           if ( curElem && theElems.find(curElem) == theElems.end() &&
10868               ( bsc3d.get() ?
10869                 isInside( curElem, *bsc3d, aTol ) :
10870                 isInside( curElem, *aFaceClassifier, aTol )))
10871             theAffectedElems.insert( curElem );
10872         }
10873       }
10874     }
10875   }
10876   return true;
10877 }
10878
10879 //================================================================================
10880 /*!
10881   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10882   \param theElems - group of of elements (edges or faces) to be replicated
10883   \param theNodesNot - group of nodes not to replicate
10884   \param theShape - shape to detect affected elements (element which geometric center
10885   located on or inside shape).
10886   The replicated nodes should be associated to affected elements.
10887   \return TRUE if operation has been completed successfully, FALSE otherwise
10888 */
10889 //================================================================================
10890
10891 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10892                                             const TIDSortedElemSet& theNodesNot,
10893                                             const TopoDS_Shape&     theShape )
10894 {
10895   if ( theShape.IsNull() )
10896     return false;
10897
10898   const double aTol = Precision::Confusion();
10899   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10900   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10901   if ( theShape.ShapeType() == TopAbs_SOLID )
10902   {
10903     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10904     bsc3d->PerformInfinitePoint(aTol);
10905   }
10906   else if (theShape.ShapeType() == TopAbs_FACE )
10907   {
10908     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10909   }
10910
10911   // iterates on indicated elements and get elements by back references from their nodes
10912   TIDSortedElemSet anAffected;
10913   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10914   for ( ;  elemItr != theElems.end(); ++elemItr )
10915   {
10916     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10917     if (!anElem)
10918       continue;
10919
10920     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10921     while ( nodeItr->more() )
10922     {
10923       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10924       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10925         continue;
10926       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10927       while ( backElemItr->more() )
10928       {
10929         const SMDS_MeshElement* curElem = backElemItr->next();
10930         if ( curElem && theElems.find(curElem) == theElems.end() &&
10931              ( bsc3d ?
10932                isInside( curElem, *bsc3d, aTol ) :
10933                isInside( curElem, *aFaceClassifier, aTol )))
10934           anAffected.insert( curElem );
10935       }
10936     }
10937   }
10938   return DoubleNodes( theElems, theNodesNot, anAffected );
10939 }
10940
10941 /*!
10942  *  \brief compute an oriented angle between two planes defined by four points.
10943  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10944  *  @param p0 base of the rotation axe
10945  *  @param p1 extremity of the rotation axe
10946  *  @param g1 belongs to the first plane
10947  *  @param g2 belongs to the second plane
10948  */
10949 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10950 {
10951   gp_Vec vref(p0, p1);
10952   gp_Vec v1(p0, g1);
10953   gp_Vec v2(p0, g2);
10954   gp_Vec n1 = vref.Crossed(v1);
10955   gp_Vec n2 = vref.Crossed(v2);
10956   try {
10957     return n2.AngleWithRef(n1, vref);
10958   }
10959   catch ( Standard_Failure ) {
10960   }
10961   return Max( v1.Magnitude(), v2.Magnitude() );
10962 }
10963
10964 /*!
10965  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10966  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10967  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10968  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10969  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10970  * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
10971  * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
10972  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10973  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10974  * \param theElems - list of groups of volumes, where a group of volume is a set of
10975  *        SMDS_MeshElements sorted by Id.
10976  * \param createJointElems - if TRUE, create the elements
10977  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10978  *        the boundary between \a theDomains and the rest mesh
10979  * \return TRUE if operation has been completed successfully, FALSE otherwise
10980  */
10981 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10982                                                      bool                                 createJointElems,
10983                                                      bool                                 onAllBoundaries)
10984 {
10985   MESSAGE("----------------------------------------------");
10986   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10987   MESSAGE("----------------------------------------------");
10988
10989   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10990   meshDS->BuildDownWardConnectivity(true);
10991   CHRONO(50);
10992   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10993
10994   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10995   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10996   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10997
10998   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10999   std::map<int,int>celldom; // cell vtkId --> domain
11000   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11001   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11002   faceDomains.clear();
11003   celldom.clear();
11004   cellDomains.clear();
11005   nodeDomains.clear();
11006   std::map<int,int> emptyMap;
11007   std::set<int> emptySet;
11008   emptyMap.clear();
11009
11010   MESSAGE(".. Number of domains :"<<theElems.size());
11011
11012   TIDSortedElemSet theRestDomElems;
11013   const int iRestDom  = -1;
11014   const int idom0     = onAllBoundaries ? iRestDom : 0;
11015   const int nbDomains = theElems.size();
11016
11017   // Check if the domains do not share an element
11018   for (int idom = 0; idom < nbDomains-1; idom++)
11019   {
11020     //       MESSAGE("... Check of domain #" << idom);
11021     const TIDSortedElemSet& domain = theElems[idom];
11022     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11023     for (; elemItr != domain.end(); ++elemItr)
11024     {
11025       const SMDS_MeshElement* anElem = *elemItr;
11026       int idombisdeb = idom + 1 ;
11027       // check if the element belongs to a domain further in the list
11028       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11029       {
11030         const TIDSortedElemSet& domainbis = theElems[idombis];
11031         if ( domainbis.count( anElem ))
11032         {
11033           MESSAGE(".... Domain #" << idom);
11034           MESSAGE(".... Domain #" << idombis);
11035           throw SALOME_Exception("The domains are not disjoint.");
11036           return false ;
11037         }
11038       }
11039     }
11040   }
11041
11042   for (int idom = 0; idom < nbDomains; idom++)
11043   {
11044
11045     // --- build a map (face to duplicate --> volume to modify)
11046     //     with all the faces shared by 2 domains (group of elements)
11047     //     and corresponding volume of this domain, for each shared face.
11048     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11049
11050     MESSAGE("... Neighbors of domain #" << idom);
11051     const TIDSortedElemSet& domain = theElems[idom];
11052     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11053     for (; elemItr != domain.end(); ++elemItr)
11054     {
11055       const SMDS_MeshElement* anElem = *elemItr;
11056       if (!anElem)
11057         continue;
11058       int vtkId = anElem->getVtkId();
11059       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11060       int neighborsVtkIds[NBMAXNEIGHBORS];
11061       int downIds[NBMAXNEIGHBORS];
11062       unsigned char downTypes[NBMAXNEIGHBORS];
11063       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11064       for (int n = 0; n < nbNeighbors; n++)
11065       {
11066         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11067         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11068         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11069         {
11070           bool ok = false;
11071           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11072           {
11073             // MESSAGE("Domain " << idombis);
11074             const TIDSortedElemSet& domainbis = theElems[idombis];
11075             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11076           }
11077           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11078           {
11079             DownIdType face(downIds[n], downTypes[n]);
11080             if (!faceDomains[face].count(idom))
11081             {
11082               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11083               celldom[vtkId] = idom;
11084               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11085             }
11086             if ( !ok )
11087             {
11088               theRestDomElems.insert( elem );
11089               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11090               celldom[neighborsVtkIds[n]] = iRestDom;
11091             }
11092           }
11093         }
11094       }
11095     }
11096   }
11097
11098   //MESSAGE("Number of shared faces " << faceDomains.size());
11099   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11100
11101   // --- explore the shared faces domain by domain,
11102   //     explore the nodes of the face and see if they belong to a cell in the domain,
11103   //     which has only a node or an edge on the border (not a shared face)
11104
11105   for (int idomain = idom0; idomain < nbDomains; idomain++)
11106   {
11107     //MESSAGE("Domain " << idomain);
11108     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11109     itface = faceDomains.begin();
11110     for (; itface != faceDomains.end(); ++itface)
11111     {
11112       const std::map<int, int>& domvol = itface->second;
11113       if (!domvol.count(idomain))
11114         continue;
11115       DownIdType face = itface->first;
11116       //MESSAGE(" --- face " << face.cellId);
11117       std::set<int> oldNodes;
11118       oldNodes.clear();
11119       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11120       std::set<int>::iterator itn = oldNodes.begin();
11121       for (; itn != oldNodes.end(); ++itn)
11122       {
11123         int oldId = *itn;
11124         //MESSAGE("     node " << oldId);
11125         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11126         for (int i=0; i<l.ncells; i++)
11127         {
11128           int vtkId = l.cells[i];
11129           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11130           if (!domain.count(anElem))
11131             continue;
11132           int vtkType = grid->GetCellType(vtkId);
11133           int downId = grid->CellIdToDownId(vtkId);
11134           if (downId < 0)
11135           {
11136             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11137             continue; // not OK at this stage of the algorithm:
11138             //no cells created after BuildDownWardConnectivity
11139           }
11140           DownIdType aCell(downId, vtkType);
11141           cellDomains[aCell][idomain] = vtkId;
11142           celldom[vtkId] = idomain;
11143           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11144         }
11145       }
11146     }
11147   }
11148
11149   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11150   //     for each shared face, get the nodes
11151   //     for each node, for each domain of the face, create a clone of the node
11152
11153   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11154   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11155   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11156
11157   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11158   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11159   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11160
11161   MESSAGE(".. Duplication of the nodes");
11162   for (int idomain = idom0; idomain < nbDomains; idomain++)
11163   {
11164     itface = faceDomains.begin();
11165     for (; itface != faceDomains.end(); ++itface)
11166     {
11167       const std::map<int, int>& domvol = itface->second;
11168       if (!domvol.count(idomain))
11169         continue;
11170       DownIdType face = itface->first;
11171       //MESSAGE(" --- face " << face.cellId);
11172       std::set<int> oldNodes;
11173       oldNodes.clear();
11174       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11175       std::set<int>::iterator itn = oldNodes.begin();
11176       for (; itn != oldNodes.end(); ++itn)
11177       {
11178         int oldId = *itn;
11179         if (nodeDomains[oldId].empty())
11180         {
11181           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11182           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11183         }
11184         std::map<int, int>::const_iterator itdom = domvol.begin();
11185         for (; itdom != domvol.end(); ++itdom)
11186         {
11187           int idom = itdom->first;
11188           //MESSAGE("         domain " << idom);
11189           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11190           {
11191             if (nodeDomains[oldId].size() >= 2) // a multiple node
11192             {
11193               vector<int> orderedDoms;
11194               //MESSAGE("multiple node " << oldId);
11195               if (mutipleNodes.count(oldId))
11196                 orderedDoms = mutipleNodes[oldId];
11197               else
11198               {
11199                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11200                 for (; it != nodeDomains[oldId].end(); ++it)
11201                   orderedDoms.push_back(it->first);
11202               }
11203               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11204               //stringstream txt;
11205               //for (int i=0; i<orderedDoms.size(); i++)
11206               //  txt << orderedDoms[i] << " ";
11207               //MESSAGE("orderedDoms " << txt.str());
11208               mutipleNodes[oldId] = orderedDoms;
11209             }
11210             double *coords = grid->GetPoint(oldId);
11211             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11212             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11213             int newId = newNode->getVtkId();
11214             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11215             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11216           }
11217         }
11218       }
11219     }
11220   }
11221
11222   MESSAGE(".. Creation of elements");
11223   for (int idomain = idom0; idomain < nbDomains; idomain++)
11224   {
11225     itface = faceDomains.begin();
11226     for (; itface != faceDomains.end(); ++itface)
11227     {
11228       std::map<int, int> domvol = itface->second;
11229       if (!domvol.count(idomain))
11230         continue;
11231       DownIdType face = itface->first;
11232       //MESSAGE(" --- face " << face.cellId);
11233       std::set<int> oldNodes;
11234       oldNodes.clear();
11235       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11236       int nbMultipleNodes = 0;
11237       std::set<int>::iterator itn = oldNodes.begin();
11238       for (; itn != oldNodes.end(); ++itn)
11239       {
11240         int oldId = *itn;
11241         if (mutipleNodes.count(oldId))
11242           nbMultipleNodes++;
11243       }
11244       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11245       {
11246         //MESSAGE("multiple Nodes detected on a shared face");
11247         int downId = itface->first.cellId;
11248         unsigned char cellType = itface->first.cellType;
11249         // --- shared edge or shared face ?
11250         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11251         {
11252           int nodes[3];
11253           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11254           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11255             if (mutipleNodes.count(nodes[i]))
11256               if (!mutipleNodesToFace.count(nodes[i]))
11257                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11258         }
11259         else // shared face (between two volumes)
11260         {
11261           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11262           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11263           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11264           for (int ie =0; ie < nbEdges; ie++)
11265           {
11266             int nodes[3];
11267             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11268             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11269             {
11270               vector<int> vn0 = mutipleNodes[nodes[0]];
11271               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11272               vector<int> doms;
11273               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11274                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11275                   if ( vn0[i0] == vn1[i1] )
11276                     doms.push_back( vn0[ i0 ]);
11277               if ( doms.size() > 2 )
11278               {
11279                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11280                 double *coords = grid->GetPoint(nodes[0]);
11281                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11282                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11283                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11284                 gp_Pnt gref;
11285                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11286                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11287                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11288                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11289                 for ( size_t id = 0; id < doms.size(); id++ )
11290                 {
11291                   int idom = doms[id];
11292                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11293                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11294                   {
11295                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11296                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11297                     if (domain.count(elem))
11298                     {
11299                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11300                       domvol[idom] = svol;
11301                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11302                       double values[3];
11303                       vtkIdType npts = 0;
11304                       vtkIdType* pts = 0;
11305                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11306                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11307                       if (id ==0)
11308                       {
11309                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11310                         angleDom[idom] = 0;
11311                       }
11312                       else
11313                       {
11314                         gp_Pnt g(values[0], values[1], values[2]);
11315                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11316                         //MESSAGE("  angle=" << angleDom[idom]);
11317                       }
11318                       break;
11319                     }
11320                   }
11321                 }
11322                 map<double, int> sortedDom; // sort domains by angle
11323                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11324                   sortedDom[ia->second] = ia->first;
11325                 vector<int> vnodes;
11326                 vector<int> vdom;
11327                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11328                 {
11329                   vdom.push_back(ib->second);
11330                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11331                 }
11332                 for (int ino = 0; ino < nbNodes; ino++)
11333                   vnodes.push_back(nodes[ino]);
11334                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11335               }
11336             }
11337           }
11338         }
11339       }
11340     }
11341   }
11342
11343   // --- iterate on shared faces (volumes to modify, face to extrude)
11344   //     get node id's of the face (id SMDS = id VTK)
11345   //     create flat element with old and new nodes if requested
11346
11347   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11348   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11349
11350   std::map<int, std::map<long,int> > nodeQuadDomains;
11351   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11352
11353   MESSAGE(".. Creation of elements: simple junction");
11354   if (createJointElems)
11355   {
11356     int idg;
11357     string joints2DName = "joints2D";
11358     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11359     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11360     string joints3DName = "joints3D";
11361     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11362     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11363
11364     itface = faceDomains.begin();
11365     for (; itface != faceDomains.end(); ++itface)
11366     {
11367       DownIdType face = itface->first;
11368       std::set<int> oldNodes;
11369       std::set<int>::iterator itn;
11370       oldNodes.clear();
11371       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11372
11373       std::map<int, int> domvol = itface->second;
11374       std::map<int, int>::iterator itdom = domvol.begin();
11375       int dom1 = itdom->first;
11376       int vtkVolId = itdom->second;
11377       itdom++;
11378       int dom2 = itdom->first;
11379       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11380                                                        nodeQuadDomains);
11381       stringstream grpname;
11382       grpname << "j_";
11383       if (dom1 < dom2)
11384         grpname << dom1 << "_" << dom2;
11385       else
11386         grpname << dom2 << "_" << dom1;
11387       string namegrp = grpname.str();
11388       if (!mapOfJunctionGroups.count(namegrp))
11389         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11390       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11391       if (sgrp)
11392         sgrp->Add(vol->GetID());
11393       if (vol->GetType() == SMDSAbs_Volume)
11394         joints3DGrp->Add(vol->GetID());
11395       else if (vol->GetType() == SMDSAbs_Face)
11396         joints2DGrp->Add(vol->GetID());
11397     }
11398   }
11399
11400   // --- create volumes on multiple domain intersection if requested
11401   //     iterate on mutipleNodesToFace
11402   //     iterate on edgesMultiDomains
11403
11404   MESSAGE(".. Creation of elements: multiple junction");
11405   if (createJointElems)
11406   {
11407     // --- iterate on mutipleNodesToFace
11408
11409     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11410     for (; itn != mutipleNodesToFace.end(); ++itn)
11411     {
11412       int node = itn->first;
11413       vector<int> orderDom = itn->second;
11414       vector<vtkIdType> orderedNodes;
11415       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11416         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11417       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11418
11419       stringstream grpname;
11420       grpname << "m2j_";
11421       grpname << 0 << "_" << 0;
11422       int idg;
11423       string namegrp = grpname.str();
11424       if (!mapOfJunctionGroups.count(namegrp))
11425         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11426       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11427       if (sgrp)
11428         sgrp->Add(face->GetID());
11429     }
11430
11431     // --- iterate on edgesMultiDomains
11432
11433     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11434     for (; ite != edgesMultiDomains.end(); ++ite)
11435     {
11436       vector<int> nodes = ite->first;
11437       vector<int> orderDom = ite->second;
11438       vector<vtkIdType> orderedNodes;
11439       if (nodes.size() == 2)
11440       {
11441         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11442         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11443           if ( orderDom.size() == 3 )
11444             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11445               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11446           else
11447             for (int idom = orderDom.size()-1; idom >=0; idom--)
11448               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11449         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11450
11451         int idg;
11452         string namegrp = "jointsMultiples";
11453         if (!mapOfJunctionGroups.count(namegrp))
11454           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11455         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11456         if (sgrp)
11457           sgrp->Add(vol->GetID());
11458       }
11459       else
11460       {
11461         //INFOS("Quadratic multiple joints not implemented");
11462         // TODO quadratic nodes
11463       }
11464     }
11465   }
11466
11467   // --- list the explicit faces and edges of the mesh that need to be modified,
11468   //     i.e. faces and edges built with one or more duplicated nodes.
11469   //     associate these faces or edges to their corresponding domain.
11470   //     only the first domain found is kept when a face or edge is shared
11471
11472   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11473   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11474   faceOrEdgeDom.clear();
11475   feDom.clear();
11476
11477   MESSAGE(".. Modification of elements");
11478   for (int idomain = idom0; idomain < nbDomains; idomain++)
11479   {
11480     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11481     for (; itnod != nodeDomains.end(); ++itnod)
11482     {
11483       int oldId = itnod->first;
11484       //MESSAGE("     node " << oldId);
11485       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11486       for (int i = 0; i < l.ncells; i++)
11487       {
11488         int vtkId = l.cells[i];
11489         int vtkType = grid->GetCellType(vtkId);
11490         int downId = grid->CellIdToDownId(vtkId);
11491         if (downId < 0)
11492           continue; // new cells: not to be modified
11493         DownIdType aCell(downId, vtkType);
11494         int volParents[1000];
11495         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11496         for (int j = 0; j < nbvol; j++)
11497           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11498             if (!feDom.count(vtkId))
11499             {
11500               feDom[vtkId] = idomain;
11501               faceOrEdgeDom[aCell] = emptyMap;
11502               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11503               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11504               //        << " type " << vtkType << " downId " << downId);
11505             }
11506       }
11507     }
11508   }
11509
11510   // --- iterate on shared faces (volumes to modify, face to extrude)
11511   //     get node id's of the face
11512   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11513
11514   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11515   for (int m=0; m<3; m++)
11516   {
11517     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11518     itface = (*amap).begin();
11519     for (; itface != (*amap).end(); ++itface)
11520     {
11521       DownIdType face = itface->first;
11522       std::set<int> oldNodes;
11523       std::set<int>::iterator itn;
11524       oldNodes.clear();
11525       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11526       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11527       std::map<int, int> localClonedNodeIds;
11528
11529       std::map<int, int> domvol = itface->second;
11530       std::map<int, int>::iterator itdom = domvol.begin();
11531       for (; itdom != domvol.end(); ++itdom)
11532       {
11533         int idom = itdom->first;
11534         int vtkVolId = itdom->second;
11535         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11536         localClonedNodeIds.clear();
11537         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11538         {
11539           int oldId = *itn;
11540           if (nodeDomains[oldId].count(idom))
11541           {
11542             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11543             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11544           }
11545         }
11546         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11547       }
11548     }
11549   }
11550
11551   // Remove empty groups (issue 0022812)
11552   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11553   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11554   {
11555     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11556       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11557   }
11558
11559   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11560   grid->BuildLinks();
11561
11562   CHRONOSTOP(50);
11563   counters::stats();
11564   return true;
11565 }
11566
11567 /*!
11568  * \brief Double nodes on some external faces and create flat elements.
11569  * Flat elements are mainly used by some types of mechanic calculations.
11570  *
11571  * Each group of the list must be constituted of faces.
11572  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11573  * @param theElems - list of groups of faces, where a group of faces is a set of
11574  * SMDS_MeshElements sorted by Id.
11575  * @return TRUE if operation has been completed successfully, FALSE otherwise
11576  */
11577 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11578 {
11579   MESSAGE("-------------------------------------------------");
11580   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11581   MESSAGE("-------------------------------------------------");
11582
11583   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11584
11585   // --- For each group of faces
11586   //     duplicate the nodes, create a flat element based on the face
11587   //     replace the nodes of the faces by their clones
11588
11589   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11590   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11591   clonedNodes.clear();
11592   intermediateNodes.clear();
11593   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11594   mapOfJunctionGroups.clear();
11595
11596   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11597   {
11598     const TIDSortedElemSet&           domain = theElems[idom];
11599     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11600     for ( ; elemItr != domain.end(); ++elemItr )
11601     {
11602       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11603       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11604       if (!aFace)
11605         continue;
11606       // MESSAGE("aFace=" << aFace->GetID());
11607       bool isQuad = aFace->IsQuadratic();
11608       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11609
11610       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11611
11612       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11613       while (nodeIt->more())
11614       {
11615         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11616         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11617         if (isMedium)
11618           ln2.push_back(node);
11619         else
11620           ln0.push_back(node);
11621
11622         const SMDS_MeshNode* clone = 0;
11623         if (!clonedNodes.count(node))
11624         {
11625           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11626           copyPosition( node, clone );
11627           clonedNodes[node] = clone;
11628         }
11629         else
11630           clone = clonedNodes[node];
11631
11632         if (isMedium)
11633           ln3.push_back(clone);
11634         else
11635           ln1.push_back(clone);
11636
11637         const SMDS_MeshNode* inter = 0;
11638         if (isQuad && (!isMedium))
11639         {
11640           if (!intermediateNodes.count(node))
11641           {
11642             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11643             copyPosition( node, inter );
11644             intermediateNodes[node] = inter;
11645           }
11646           else
11647             inter = intermediateNodes[node];
11648           ln4.push_back(inter);
11649         }
11650       }
11651
11652       // --- extrude the face
11653
11654       vector<const SMDS_MeshNode*> ln;
11655       SMDS_MeshVolume* vol = 0;
11656       vtkIdType aType = aFace->GetVtkType();
11657       switch (aType)
11658       {
11659       case VTK_TRIANGLE:
11660         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11661         // MESSAGE("vol prism " << vol->GetID());
11662         ln.push_back(ln1[0]);
11663         ln.push_back(ln1[1]);
11664         ln.push_back(ln1[2]);
11665         break;
11666       case VTK_QUAD:
11667         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11668         // MESSAGE("vol hexa " << vol->GetID());
11669         ln.push_back(ln1[0]);
11670         ln.push_back(ln1[1]);
11671         ln.push_back(ln1[2]);
11672         ln.push_back(ln1[3]);
11673         break;
11674       case VTK_QUADRATIC_TRIANGLE:
11675         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11676                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11677         // MESSAGE("vol quad prism " << vol->GetID());
11678         ln.push_back(ln1[0]);
11679         ln.push_back(ln1[1]);
11680         ln.push_back(ln1[2]);
11681         ln.push_back(ln3[0]);
11682         ln.push_back(ln3[1]);
11683         ln.push_back(ln3[2]);
11684         break;
11685       case VTK_QUADRATIC_QUAD:
11686         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11687         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11688         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11689         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11690                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11691                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11692         // MESSAGE("vol quad hexa " << vol->GetID());
11693         ln.push_back(ln1[0]);
11694         ln.push_back(ln1[1]);
11695         ln.push_back(ln1[2]);
11696         ln.push_back(ln1[3]);
11697         ln.push_back(ln3[0]);
11698         ln.push_back(ln3[1]);
11699         ln.push_back(ln3[2]);
11700         ln.push_back(ln3[3]);
11701         break;
11702       case VTK_POLYGON:
11703         break;
11704       default:
11705         break;
11706       }
11707
11708       if (vol)
11709       {
11710         stringstream grpname;
11711         grpname << "jf_";
11712         grpname << idom;
11713         int idg;
11714         string namegrp = grpname.str();
11715         if (!mapOfJunctionGroups.count(namegrp))
11716           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11717         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11718         if (sgrp)
11719           sgrp->Add(vol->GetID());
11720       }
11721
11722       // --- modify the face
11723
11724       aFace->ChangeNodes(&ln[0], ln.size());
11725     }
11726   }
11727   return true;
11728 }
11729
11730 /*!
11731  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11732  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11733  *  groups of faces to remove inside the object, (idem edges).
11734  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11735  */
11736 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11737                                       const TopoDS_Shape&             theShape,
11738                                       SMESH_NodeSearcher*             theNodeSearcher,
11739                                       const char*                     groupName,
11740                                       std::vector<double>&            nodesCoords,
11741                                       std::vector<std::vector<int> >& listOfListOfNodes)
11742 {
11743   MESSAGE("--------------------------------");
11744   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11745   MESSAGE("--------------------------------");
11746
11747   // --- zone of volumes to remove is given :
11748   //     1 either by a geom shape (one or more vertices) and a radius,
11749   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11750   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11751   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11752   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11753   //     defined by it's name.
11754
11755   SMESHDS_GroupBase* groupDS = 0;
11756   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11757   while ( groupIt->more() )
11758   {
11759     groupDS = 0;
11760     SMESH_Group * group = groupIt->next();
11761     if ( !group ) continue;
11762     groupDS = group->GetGroupDS();
11763     if ( !groupDS || groupDS->IsEmpty() ) continue;
11764     std::string grpName = group->GetName();
11765     //MESSAGE("grpName=" << grpName);
11766     if (grpName == groupName)
11767       break;
11768     else
11769       groupDS = 0;
11770   }
11771
11772   bool isNodeGroup = false;
11773   bool isNodeCoords = false;
11774   if (groupDS)
11775   {
11776     if (groupDS->GetType() != SMDSAbs_Node)
11777       return;
11778     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11779   }
11780
11781   if (nodesCoords.size() > 0)
11782     isNodeCoords = true; // a list o nodes given by their coordinates
11783   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11784
11785   // --- define groups to build
11786
11787   int idg; // --- group of SMDS volumes
11788   string grpvName = groupName;
11789   grpvName += "_vol";
11790   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11791   if (!grp)
11792   {
11793     MESSAGE("group not created " << grpvName);
11794     return;
11795   }
11796   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11797
11798   int idgs; // --- group of SMDS faces on the skin
11799   string grpsName = groupName;
11800   grpsName += "_skin";
11801   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11802   if (!grps)
11803   {
11804     MESSAGE("group not created " << grpsName);
11805     return;
11806   }
11807   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11808
11809   int idgi; // --- group of SMDS faces internal (several shapes)
11810   string grpiName = groupName;
11811   grpiName += "_internalFaces";
11812   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11813   if (!grpi)
11814   {
11815     MESSAGE("group not created " << grpiName);
11816     return;
11817   }
11818   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11819
11820   int idgei; // --- group of SMDS faces internal (several shapes)
11821   string grpeiName = groupName;
11822   grpeiName += "_internalEdges";
11823   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11824   if (!grpei)
11825   {
11826     MESSAGE("group not created " << grpeiName);
11827     return;
11828   }
11829   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11830
11831   // --- build downward connectivity
11832
11833   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11834   meshDS->BuildDownWardConnectivity(true);
11835   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11836
11837   // --- set of volumes detected inside
11838
11839   std::set<int> setOfInsideVol;
11840   std::set<int> setOfVolToCheck;
11841
11842   std::vector<gp_Pnt> gpnts;
11843   gpnts.clear();
11844
11845   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11846   {
11847     MESSAGE("group of nodes provided");
11848     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11849     while ( elemIt->more() )
11850     {
11851       const SMDS_MeshElement* elem = elemIt->next();
11852       if (!elem)
11853         continue;
11854       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11855       if (!node)
11856         continue;
11857       SMDS_MeshElement* vol = 0;
11858       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11859       while (volItr->more())
11860       {
11861         vol = (SMDS_MeshElement*)volItr->next();
11862         setOfInsideVol.insert(vol->getVtkId());
11863         sgrp->Add(vol->GetID());
11864       }
11865     }
11866   }
11867   else if (isNodeCoords)
11868   {
11869     MESSAGE("list of nodes coordinates provided");
11870     size_t i = 0;
11871     int k = 0;
11872     while ( i < nodesCoords.size()-2 )
11873     {
11874       double x = nodesCoords[i++];
11875       double y = nodesCoords[i++];
11876       double z = nodesCoords[i++];
11877       gp_Pnt p = gp_Pnt(x, y ,z);
11878       gpnts.push_back(p);
11879       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11880       k++;
11881     }
11882   }
11883   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11884   {
11885     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11886     TopTools_IndexedMapOfShape vertexMap;
11887     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11888     gp_Pnt p = gp_Pnt(0,0,0);
11889     if (vertexMap.Extent() < 1)
11890       return;
11891
11892     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11893     {
11894       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11895       p = BRep_Tool::Pnt(vertex);
11896       gpnts.push_back(p);
11897       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11898     }
11899   }
11900
11901   if (gpnts.size() > 0)
11902   {
11903     int nodeId = 0;
11904     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11905     if (startNode)
11906       nodeId = startNode->GetID();
11907     MESSAGE("nodeId " << nodeId);
11908
11909     double radius2 = radius*radius;
11910     MESSAGE("radius2 " << radius2);
11911
11912     // --- volumes on start node
11913
11914     setOfVolToCheck.clear();
11915     SMDS_MeshElement* startVol = 0;
11916     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11917     while (volItr->more())
11918     {
11919       startVol = (SMDS_MeshElement*)volItr->next();
11920       setOfVolToCheck.insert(startVol->getVtkId());
11921     }
11922     if (setOfVolToCheck.empty())
11923     {
11924       MESSAGE("No volumes found");
11925       return;
11926     }
11927
11928     // --- starting with central volumes then their neighbors, check if they are inside
11929     //     or outside the domain, until no more new neighbor volume is inside.
11930     //     Fill the group of inside volumes
11931
11932     std::map<int, double> mapOfNodeDistance2;
11933     mapOfNodeDistance2.clear();
11934     std::set<int> setOfOutsideVol;
11935     while (!setOfVolToCheck.empty())
11936     {
11937       std::set<int>::iterator it = setOfVolToCheck.begin();
11938       int vtkId = *it;
11939       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11940       bool volInside = false;
11941       vtkIdType npts = 0;
11942       vtkIdType* pts = 0;
11943       grid->GetCellPoints(vtkId, npts, pts);
11944       for (int i=0; i<npts; i++)
11945       {
11946         double distance2 = 0;
11947         if (mapOfNodeDistance2.count(pts[i]))
11948         {
11949           distance2 = mapOfNodeDistance2[pts[i]];
11950           MESSAGE("point " << pts[i] << " distance2 " << distance2);
11951         }
11952         else
11953         {
11954           double *coords = grid->GetPoint(pts[i]);
11955           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11956           distance2 = 1.E40;
11957           for ( size_t j = 0; j < gpnts.size(); j++ )
11958           {
11959             double d2 = aPoint.SquareDistance( gpnts[ j ]);
11960             if (d2 < distance2)
11961             {
11962               distance2 = d2;
11963               if (distance2 < radius2)
11964                 break;
11965             }
11966           }
11967           mapOfNodeDistance2[pts[i]] = distance2;
11968           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11969         }
11970         if (distance2 < radius2)
11971         {
11972           volInside = true; // one or more nodes inside the domain
11973           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11974           break;
11975         }
11976       }
11977       if (volInside)
11978       {
11979         setOfInsideVol.insert(vtkId);
11980         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11981         int neighborsVtkIds[NBMAXNEIGHBORS];
11982         int downIds[NBMAXNEIGHBORS];
11983         unsigned char downTypes[NBMAXNEIGHBORS];
11984         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11985         for (int n = 0; n < nbNeighbors; n++)
11986           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11987             setOfVolToCheck.insert(neighborsVtkIds[n]);
11988       }
11989       else
11990       {
11991         setOfOutsideVol.insert(vtkId);
11992         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11993       }
11994       setOfVolToCheck.erase(vtkId);
11995     }
11996   }
11997
11998   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11999   //     If yes, add the volume to the inside set
12000
12001   bool addedInside = true;
12002   std::set<int> setOfVolToReCheck;
12003   while (addedInside)
12004   {
12005     MESSAGE(" --------------------------- re check");
12006     addedInside = false;
12007     std::set<int>::iterator itv = setOfInsideVol.begin();
12008     for (; itv != setOfInsideVol.end(); ++itv)
12009     {
12010       int vtkId = *itv;
12011       int neighborsVtkIds[NBMAXNEIGHBORS];
12012       int downIds[NBMAXNEIGHBORS];
12013       unsigned char downTypes[NBMAXNEIGHBORS];
12014       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12015       for (int n = 0; n < nbNeighbors; n++)
12016         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12017           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12018     }
12019     setOfVolToCheck = setOfVolToReCheck;
12020     setOfVolToReCheck.clear();
12021     while  (!setOfVolToCheck.empty())
12022     {
12023       std::set<int>::iterator it = setOfVolToCheck.begin();
12024       int vtkId = *it;
12025       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12026       {
12027         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12028         int countInside = 0;
12029         int neighborsVtkIds[NBMAXNEIGHBORS];
12030         int downIds[NBMAXNEIGHBORS];
12031         unsigned char downTypes[NBMAXNEIGHBORS];
12032         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12033         for (int n = 0; n < nbNeighbors; n++)
12034           if (setOfInsideVol.count(neighborsVtkIds[n]))
12035             countInside++;
12036         MESSAGE("countInside " << countInside);
12037         if (countInside > 1)
12038         {
12039           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12040           setOfInsideVol.insert(vtkId);
12041           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12042           addedInside = true;
12043         }
12044         else
12045           setOfVolToReCheck.insert(vtkId);
12046       }
12047       setOfVolToCheck.erase(vtkId);
12048     }
12049   }
12050
12051   // --- map of Downward faces at the boundary, inside the global volume
12052   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12053   //     fill group of SMDS faces inside the volume (when several volume shapes)
12054   //     fill group of SMDS faces on the skin of the global volume (if skin)
12055
12056   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12057   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12058   std::set<int>::iterator it = setOfInsideVol.begin();
12059   for (; it != setOfInsideVol.end(); ++it)
12060   {
12061     int vtkId = *it;
12062     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12063     int neighborsVtkIds[NBMAXNEIGHBORS];
12064     int downIds[NBMAXNEIGHBORS];
12065     unsigned char downTypes[NBMAXNEIGHBORS];
12066     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12067     for (int n = 0; n < nbNeighbors; n++)
12068     {
12069       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12070       if (neighborDim == 3)
12071       {
12072         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12073         {
12074           DownIdType face(downIds[n], downTypes[n]);
12075           boundaryFaces[face] = vtkId;
12076         }
12077         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12078         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12079         if (vtkFaceId >= 0)
12080         {
12081           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12082           // find also the smds edges on this face
12083           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12084           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12085           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12086           for (int i = 0; i < nbEdges; i++)
12087           {
12088             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12089             if (vtkEdgeId >= 0)
12090               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12091           }
12092         }
12093       }
12094       else if (neighborDim == 2) // skin of the volume
12095       {
12096         DownIdType face(downIds[n], downTypes[n]);
12097         skinFaces[face] = vtkId;
12098         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12099         if (vtkFaceId >= 0)
12100           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12101       }
12102     }
12103   }
12104
12105   // --- identify the edges constituting the wire of each subshape on the skin
12106   //     define polylines with the nodes of edges, equivalent to wires
12107   //     project polylines on subshapes, and partition, to get geom faces
12108
12109   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12110   std::set<int> emptySet;
12111   emptySet.clear();
12112   std::set<int> shapeIds;
12113
12114   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12115   while (itelem->more())
12116   {
12117     const SMDS_MeshElement *elem = itelem->next();
12118     int shapeId = elem->getshapeId();
12119     int vtkId = elem->getVtkId();
12120     if (!shapeIdToVtkIdSet.count(shapeId))
12121     {
12122       shapeIdToVtkIdSet[shapeId] = emptySet;
12123       shapeIds.insert(shapeId);
12124     }
12125     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12126   }
12127
12128   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12129   std::set<DownIdType, DownIdCompare> emptyEdges;
12130   emptyEdges.clear();
12131
12132   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12133   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12134   {
12135     int shapeId = itShape->first;
12136     MESSAGE(" --- Shape ID --- "<< shapeId);
12137     shapeIdToEdges[shapeId] = emptyEdges;
12138
12139     std::vector<int> nodesEdges;
12140
12141     std::set<int>::iterator its = itShape->second.begin();
12142     for (; its != itShape->second.end(); ++its)
12143     {
12144       int vtkId = *its;
12145       MESSAGE("     " << vtkId);
12146       int neighborsVtkIds[NBMAXNEIGHBORS];
12147       int downIds[NBMAXNEIGHBORS];
12148       unsigned char downTypes[NBMAXNEIGHBORS];
12149       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12150       for (int n = 0; n < nbNeighbors; n++)
12151       {
12152         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12153           continue;
12154         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12155         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12156         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12157         {
12158           DownIdType edge(downIds[n], downTypes[n]);
12159           if (!shapeIdToEdges[shapeId].count(edge))
12160           {
12161             shapeIdToEdges[shapeId].insert(edge);
12162             int vtkNodeId[3];
12163             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12164             nodesEdges.push_back(vtkNodeId[0]);
12165             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12166             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12167           }
12168         }
12169       }
12170     }
12171
12172     std::list<int> order;
12173     order.clear();
12174     if (nodesEdges.size() > 0)
12175     {
12176       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12177       nodesEdges[0] = -1;
12178       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12179       nodesEdges[1] = -1; // do not reuse this edge
12180       bool found = true;
12181       while (found)
12182       {
12183         int nodeTofind = order.back(); // try first to push back
12184         int i = 0;
12185         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12186           if (nodesEdges[i] == nodeTofind)
12187             break;
12188         if ( i == (int) nodesEdges.size() )
12189           found = false; // no follower found on back
12190         else
12191         {
12192           if (i%2) // odd ==> use the previous one
12193             if (nodesEdges[i-1] < 0)
12194               found = false;
12195             else
12196             {
12197               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12198               nodesEdges[i-1] = -1;
12199             }
12200           else // even ==> use the next one
12201             if (nodesEdges[i+1] < 0)
12202               found = false;
12203             else
12204             {
12205               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12206               nodesEdges[i+1] = -1;
12207             }
12208         }
12209         if (found)
12210           continue;
12211         // try to push front
12212         found = true;
12213         nodeTofind = order.front(); // try to push front
12214         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12215           if ( nodesEdges[i] == nodeTofind )
12216             break;
12217         if ( i == (int)nodesEdges.size() )
12218         {
12219           found = false; // no predecessor found on front
12220           continue;
12221         }
12222         if (i%2) // odd ==> use the previous one
12223           if (nodesEdges[i-1] < 0)
12224             found = false;
12225           else
12226           {
12227             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12228             nodesEdges[i-1] = -1;
12229           }
12230         else // even ==> use the next one
12231           if (nodesEdges[i+1] < 0)
12232             found = false;
12233           else
12234           {
12235             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12236             nodesEdges[i+1] = -1;
12237           }
12238       }
12239     }
12240
12241
12242     std::vector<int> nodes;
12243     nodes.push_back(shapeId);
12244     std::list<int>::iterator itl = order.begin();
12245     for (; itl != order.end(); itl++)
12246     {
12247       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12248       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12249     }
12250     listOfListOfNodes.push_back(nodes);
12251   }
12252
12253   //     partition geom faces with blocFissure
12254   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12255   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12256
12257   return;
12258 }
12259
12260
12261 //================================================================================
12262 /*!
12263  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12264  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12265  * \return TRUE if operation has been completed successfully, FALSE otherwise
12266  */
12267 //================================================================================
12268
12269 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12270 {
12271   // iterates on volume elements and detect all free faces on them
12272   SMESHDS_Mesh* aMesh = GetMeshDS();
12273   if (!aMesh)
12274     return false;
12275
12276   ElemFeatures faceType( SMDSAbs_Face );
12277   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12278   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12279   while(vIt->more())
12280   {
12281     const SMDS_MeshVolume* volume = vIt->next();
12282     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12283     vTool.SetExternalNormal();
12284     const int iQuad = volume->IsQuadratic();
12285     faceType.SetQuad( iQuad );
12286     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12287     {
12288       if (!vTool.IsFreeFace(iface))
12289         continue;
12290       nbFree++;
12291       vector<const SMDS_MeshNode *> nodes;
12292       int nbFaceNodes = vTool.NbFaceNodes(iface);
12293       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12294       int inode = 0;
12295       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12296         nodes.push_back(faceNodes[inode]);
12297
12298       if (iQuad) // add medium nodes
12299       {
12300         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12301           nodes.push_back(faceNodes[inode]);
12302         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12303           nodes.push_back(faceNodes[8]);
12304       }
12305       // add new face based on volume nodes
12306       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12307       {
12308         nbExisted++; // face already exsist
12309       }
12310       else
12311       {
12312         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12313         nbCreated++;
12314       }
12315     }
12316   }
12317   return ( nbFree == ( nbExisted + nbCreated ));
12318 }
12319
12320 namespace
12321 {
12322   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12323   {
12324     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12325       return n;
12326     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12327   }
12328 }
12329 //================================================================================
12330 /*!
12331  * \brief Creates missing boundary elements
12332  *  \param elements - elements whose boundary is to be checked
12333  *  \param dimension - defines type of boundary elements to create
12334  *  \param group - a group to store created boundary elements in
12335  *  \param targetMesh - a mesh to store created boundary elements in
12336  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12337  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12338  *                                boundary elements will be copied into the targetMesh
12339  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12340  *                                boundary elements will be added into the new group
12341  *  \param aroundElements - if true, elements will be created on boundary of given
12342  *                          elements else, on boundary of the whole mesh.
12343  * \return nb of added boundary elements
12344  */
12345 //================================================================================
12346
12347 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12348                                        Bnd_Dimension           dimension,
12349                                        SMESH_Group*            group/*=0*/,
12350                                        SMESH_Mesh*             targetMesh/*=0*/,
12351                                        bool                    toCopyElements/*=false*/,
12352                                        bool                    toCopyExistingBoundary/*=false*/,
12353                                        bool                    toAddExistingBondary/*= false*/,
12354                                        bool                    aroundElements/*= false*/)
12355 {
12356   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12357   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12358   // hope that all elements are of the same type, do not check them all
12359   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12360     throw SALOME_Exception(LOCALIZED("wrong element type"));
12361
12362   if ( !targetMesh )
12363     toCopyElements = toCopyExistingBoundary = false;
12364
12365   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12366   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12367   int nbAddedBnd = 0;
12368
12369   // editor adding present bnd elements and optionally holding elements to add to the group
12370   SMESH_MeshEditor* presentEditor;
12371   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12372   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12373
12374   SMESH_MesherHelper helper( *myMesh );
12375   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12376   SMDS_VolumeTool vTool;
12377   TIDSortedElemSet avoidSet;
12378   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12379   size_t inode;
12380
12381   typedef vector<const SMDS_MeshNode*> TConnectivity;
12382   TConnectivity tgtNodes;
12383   ElemFeatures elemKind( missType ), elemToCopy;
12384
12385   vector<const SMDS_MeshElement*> presentBndElems;
12386   vector<TConnectivity>           missingBndElems;
12387   vector<int>                     freeFacets;
12388   TConnectivity nodes, elemNodes;
12389
12390   SMDS_ElemIteratorPtr eIt;
12391   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12392   else                  eIt = elemSetIterator( elements );
12393
12394   while (eIt->more())
12395   {
12396     const SMDS_MeshElement* elem = eIt->next();
12397     const int              iQuad = elem->IsQuadratic();
12398     elemKind.SetQuad( iQuad );
12399
12400     // ------------------------------------------------------------------------------------
12401     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12402     // ------------------------------------------------------------------------------------
12403     presentBndElems.clear();
12404     missingBndElems.clear();
12405     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12406     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12407     {
12408       const SMDS_MeshElement* otherVol = 0;
12409       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12410       {
12411         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12412              ( !aroundElements || elements.count( otherVol )))
12413           continue;
12414         freeFacets.push_back( iface );
12415       }
12416       if ( missType == SMDSAbs_Face )
12417         vTool.SetExternalNormal();
12418       for ( size_t i = 0; i < freeFacets.size(); ++i )
12419       {
12420         int                iface = freeFacets[i];
12421         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12422         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12423         if ( missType == SMDSAbs_Edge ) // boundary edges
12424         {
12425           nodes.resize( 2+iQuad );
12426           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12427           {
12428             for ( size_t j = 0; j < nodes.size(); ++j )
12429               nodes[ j ] = nn[ i+j ];
12430             if ( const SMDS_MeshElement* edge =
12431                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12432               presentBndElems.push_back( edge );
12433             else
12434               missingBndElems.push_back( nodes );
12435           }
12436         }
12437         else // boundary face
12438         {
12439           nodes.clear();
12440           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12441             nodes.push_back( nn[inode] ); // add corner nodes
12442           if (iQuad)
12443             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12444               nodes.push_back( nn[inode] ); // add medium nodes
12445           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12446           if ( iCenter > 0 )
12447             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12448
12449           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12450                                                                SMDSAbs_Face, /*noMedium=*/false ))
12451             presentBndElems.push_back( f );
12452           else
12453             missingBndElems.push_back( nodes );
12454
12455           if ( targetMesh != myMesh )
12456           {
12457             // add 1D elements on face boundary to be added to a new mesh
12458             const SMDS_MeshElement* edge;
12459             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12460             {
12461               if ( iQuad )
12462                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12463               else
12464                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12465               if ( edge && avoidSet.insert( edge ).second )
12466                 presentBndElems.push_back( edge );
12467             }
12468           }
12469         }
12470       }
12471     }
12472     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12473     {
12474       avoidSet.clear(), avoidSet.insert( elem );
12475       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12476                         SMDS_MeshElement::iterator() );
12477       elemNodes.push_back( elemNodes[0] );
12478       nodes.resize( 2 + iQuad );
12479       const int nbLinks = elem->NbCornerNodes();
12480       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12481       {
12482         nodes[0] = elemNodes[iN];
12483         nodes[1] = elemNodes[iN+1+iQuad];
12484         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12485           continue; // not free link
12486
12487         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12488         if ( const SMDS_MeshElement* edge =
12489              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12490           presentBndElems.push_back( edge );
12491         else
12492           missingBndElems.push_back( nodes );
12493       }
12494     }
12495
12496     // ---------------------------------
12497     // 2. Add missing boundary elements
12498     // ---------------------------------
12499     if ( targetMesh != myMesh )
12500       // instead of making a map of nodes in this mesh and targetMesh,
12501       // we create nodes with same IDs.
12502       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12503       {
12504         TConnectivity& srcNodes = missingBndElems[i];
12505         tgtNodes.resize( srcNodes.size() );
12506         for ( inode = 0; inode < srcNodes.size(); ++inode )
12507           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12508         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12509                                                                    missType,
12510                                                                    /*noMedium=*/false))
12511           continue;
12512         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12513         ++nbAddedBnd;
12514       }
12515     else
12516       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12517       {
12518         TConnectivity& nodes = missingBndElems[ i ];
12519         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12520                                                                    missType,
12521                                                                    /*noMedium=*/false))
12522           continue;
12523         SMDS_MeshElement* newElem =
12524           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12525         nbAddedBnd += bool( newElem );
12526
12527         // try to set a new element to a shape
12528         if ( myMesh->HasShapeToMesh() )
12529         {
12530           bool ok = true;
12531           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12532           const size_t nbN = nodes.size() / (iQuad+1 );
12533           for ( inode = 0; inode < nbN && ok; ++inode )
12534           {
12535             pair<int, TopAbs_ShapeEnum> i_stype =
12536               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12537             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12538               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12539           }
12540           if ( ok && mediumShapes.size() > 1 )
12541           {
12542             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12543             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12544             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12545             {
12546               if (( ok = ( stype_i->first != stype_i_0.first )))
12547                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12548                                         aMesh->IndexToShape( stype_i_0.second ));
12549             }
12550           }
12551           if ( ok && mediumShapes.begin()->first == missShapeType )
12552             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12553         }
12554       }
12555
12556     // ----------------------------------
12557     // 3. Copy present boundary elements
12558     // ----------------------------------
12559     if ( toCopyExistingBoundary )
12560       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12561       {
12562         const SMDS_MeshElement* e = presentBndElems[i];
12563         tgtNodes.resize( e->NbNodes() );
12564         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12565           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12566         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12567       }
12568     else // store present elements to add them to a group
12569       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12570       {
12571         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12572       }
12573
12574   } // loop on given elements
12575
12576   // ---------------------------------------------
12577   // 4. Fill group with boundary elements
12578   // ---------------------------------------------
12579   if ( group )
12580   {
12581     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12582       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12583         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12584   }
12585   tgtEditor.myLastCreatedElems.Clear();
12586   tgtEditor2.myLastCreatedElems.Clear();
12587
12588   // -----------------------
12589   // 5. Copy given elements
12590   // -----------------------
12591   if ( toCopyElements && targetMesh != myMesh )
12592   {
12593     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12594     else                  eIt = elemSetIterator( elements );
12595     while (eIt->more())
12596     {
12597       const SMDS_MeshElement* elem = eIt->next();
12598       tgtNodes.resize( elem->NbNodes() );
12599       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12600         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12601       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12602
12603       tgtEditor.myLastCreatedElems.Clear();
12604     }
12605   }
12606   return nbAddedBnd;
12607 }
12608
12609 //================================================================================
12610 /*!
12611  * \brief Copy node position and set \a to node on the same geometry
12612  */
12613 //================================================================================
12614
12615 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12616                                      const SMDS_MeshNode* to )
12617 {
12618   if ( !from || !to ) return;
12619
12620   SMDS_PositionPtr pos = from->GetPosition();
12621   if ( !pos || from->getshapeId() < 1 ) return;
12622
12623   switch ( pos->GetTypeOfPosition() )
12624   {
12625   case SMDS_TOP_3DSPACE: break;
12626
12627   case SMDS_TOP_FACE:
12628   {
12629     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12630     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12631                                 fPos->GetUParameter(), fPos->GetVParameter() );
12632     break;
12633   }
12634   case SMDS_TOP_EDGE:
12635   {
12636     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12637     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12638     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12639     break;
12640   }
12641   case SMDS_TOP_VERTEX:
12642   {
12643     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12644     break;
12645   }
12646   case SMDS_TOP_UNSPEC:
12647   default:;
12648   }
12649 }