Salome HOME
3094dd2f3ae37af22107b89228561f2002e5f99a
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  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   MESSAGE("InverseDiag");
687   myLastCreatedElems.Clear();
688   myLastCreatedNodes.Clear();
689
690   if (!theTria1 || !theTria2)
691     return false;
692
693   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694   if (!F1) return false;
695   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696   if (!F2) return false;
697   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
699
700     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
702     //    |/ |                                         | \|
703     //  B +--+ 2                                     B +--+ 2
704
705     // put nodes in array and find out indices of the same ones
706     const SMDS_MeshNode* aNodes [6];
707     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
708     int i = 0;
709     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710     while ( it->more() ) {
711       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
712
713       if ( i > 2 ) // theTria2
714         // find same node of theTria1
715         for ( int j = 0; j < 3; j++ )
716           if ( aNodes[ i ] == aNodes[ j ]) {
717             sameInd[ j ] = i;
718             sameInd[ i ] = j;
719             break;
720           }
721       // next
722       i++;
723       if ( i == 3 ) {
724         if ( it->more() )
725           return false; // theTria1 is not a triangle
726         it = theTria2->nodesIterator();
727       }
728       if ( i == 6 && it->more() )
729         return false; // theTria2 is not a triangle
730     }
731
732     // find indices of 1,2 and of A,B in theTria1
733     int iA = -1, iB = 0, i1 = 0, i2 = 0;
734     for ( i = 0; i < 6; i++ ) {
735       if ( sameInd [ i ] == -1 ) {
736         if ( i < 3 ) i1 = i;
737         else         i2 = i;
738       }
739       else if (i < 3) {
740         if ( iA >= 0) iB = i;
741         else          iA = i;
742       }
743     }
744     // nodes 1 and 2 should not be the same
745     if ( aNodes[ i1 ] == aNodes[ i2 ] )
746       return false;
747
748     // theTria1: A->2
749     aNodes[ iA ] = aNodes[ i2 ];
750     // theTria2: B->1
751     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
752
753     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
755
756     return true;
757
758   } // end if(F1 && F2)
759
760   // check case of quadratic faces
761   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
763     return false;
764   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
766     return false;
767
768   //       5
769   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
771   //    |   / |
772   //  7 +  +  + 6
773   //    | /9  |
774   //    |/    |
775   //  4 +--+--+ 3
776   //       8
777
778   vector< const SMDS_MeshNode* > N1;
779   vector< const SMDS_MeshNode* > N2;
780   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
781     return false;
782   // now we receive following N1 and N2 (using numeration as above image)
783   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
784   // i.e. first nodes from both arrays determ new diagonal
785
786   vector< const SMDS_MeshNode*> N1new( N1.size() );
787   vector< const SMDS_MeshNode*> N2new( N2.size() );
788   N1new.back() = N1.back(); // central node of biquadratic
789   N2new.back() = N2.back();
790   N1new[0] = N1[0];  N2new[0] = N1[0];
791   N1new[1] = N2[0];  N2new[1] = N1[1];
792   N1new[2] = N2[1];  N2new[2] = N2[0];
793   N1new[3] = N1[4];  N2new[3] = N1[3];
794   N1new[4] = N2[3];  N2new[4] = N2[5];
795   N1new[5] = N1[5];  N2new[5] = N1[4];
796   // change nodes in faces
797   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
799
800   // move the central node of biquadratic triangle
801   SMESH_MesherHelper helper( *GetMesh() );
802   for ( int is2nd = 0; is2nd < 2; ++is2nd )
803   {
804     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
805     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806     if ( nodes.size() < 7 )
807       continue;
808     helper.SetSubShape( tria->getshapeId() );
809     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
810     gp_Pnt xyz;
811     if ( F.IsNull() )
812     {
813       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814               SMESH_TNodeXYZ( nodes[4] ) +
815               SMESH_TNodeXYZ( nodes[5] )) / 3.;
816     }
817     else
818     {
819       bool checkUV;
820       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
823       TopLoc_Location loc;
824       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825       xyz = S->Value( uv.X(), uv.Y() );
826       xyz.Transform( loc );
827       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
828            nodes[6]->getshapeId() > 0 )
829         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
830     }
831     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
832   }
833   return true;
834 }
835
836 //=======================================================================
837 //function : findTriangles
838 //purpose  : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
840
841 static bool findTriangles(const SMDS_MeshNode *    theNode1,
842                           const SMDS_MeshNode *    theNode2,
843                           const SMDS_MeshElement*& theTria1,
844                           const SMDS_MeshElement*& theTria2)
845 {
846   if ( !theNode1 || !theNode2 ) return false;
847
848   theTria1 = theTria2 = 0;
849
850   set< const SMDS_MeshElement* > emap;
851   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
852   while (it->more()) {
853     const SMDS_MeshElement* elem = it->next();
854     if ( elem->NbCornerNodes() == 3 )
855       emap.insert( elem );
856   }
857   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
858   while (it->more()) {
859     const SMDS_MeshElement* elem = it->next();
860     if ( emap.count( elem )) {
861       if ( !theTria1 )
862       {
863         theTria1 = elem;
864       }
865       else  
866       {
867         theTria2 = elem;
868         // theTria1 must be element with minimum ID
869         if ( theTria2->GetID() < theTria1->GetID() )
870           std::swap( theTria2, theTria1 );
871         return true;
872       }
873     }
874   }
875   return false;
876 }
877
878 //=======================================================================
879 //function : InverseDiag
880 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
881 //           with ones built on the same 4 nodes but having other common link.
882 //           Return false if proper faces not found
883 //=======================================================================
884
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886                                     const SMDS_MeshNode * theNode2)
887 {
888   myLastCreatedElems.Clear();
889   myLastCreatedNodes.Clear();
890
891   MESSAGE( "::InverseDiag()" );
892
893   const SMDS_MeshElement *tr1, *tr2;
894   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
895     return false;
896
897   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898   if (!F1) return false;
899   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900   if (!F2) return false;
901   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
903
904     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
906     //    |/ |                                    | \|
907     //  B +--+ 2                                B +--+ 2
908
909     // put nodes in array
910     // and find indices of 1,2 and of A in tr1 and of B in tr2
911     int i, iA1 = 0, i1 = 0;
912     const SMDS_MeshNode* aNodes1 [3];
913     SMDS_ElemIteratorPtr it;
914     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916       if ( aNodes1[ i ] == theNode1 )
917         iA1 = i; // node A in tr1
918       else if ( aNodes1[ i ] != theNode2 )
919         i1 = i;  // node 1
920     }
921     int iB2 = 0, i2 = 0;
922     const SMDS_MeshNode* aNodes2 [3];
923     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925       if ( aNodes2[ i ] == theNode2 )
926         iB2 = i; // node B in tr2
927       else if ( aNodes2[ i ] != theNode1 )
928         i2 = i;  // node 2
929     }
930
931     // nodes 1 and 2 should not be the same
932     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
933       return false;
934
935     // tr1: A->2
936     aNodes1[ iA1 ] = aNodes2[ i2 ];
937     // tr2: B->1
938     aNodes2[ iB2 ] = aNodes1[ i1 ];
939
940     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
942
943     return true;
944   }
945
946   // check case of quadratic faces
947   return InverseDiag(tr1,tr2);
948 }
949
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
953 //           fusion of triangles tr1 and tr2 having shared link on
954 //           theNode1 and theNode2
955 //=======================================================================
956
957 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
958                         const SMDS_MeshNode *    theNode1,
959                         const SMDS_MeshNode *    theNode2,
960                         const SMDS_MeshElement * tr1,
961                         const SMDS_MeshElement * tr2 )
962 {
963   if( tr1->NbNodes() != tr2->NbNodes() )
964     return false;
965   // find the 4-th node to insert into tr1
966   const SMDS_MeshNode* n4 = 0;
967   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
968   int i=0;
969   while ( !n4 && i<3 ) {
970     const SMDS_MeshNode * n = cast2Node( it->next() );
971     i++;
972     bool isDiag = ( n == theNode1 || n == theNode2 );
973     if ( !isDiag )
974       n4 = n;
975   }
976   // Make an array of nodes to be in a quadrangle
977   int iNode = 0, iFirstDiag = -1;
978   it = tr1->nodesIterator();
979   i=0;
980   while ( i<3 ) {
981     const SMDS_MeshNode * n = cast2Node( it->next() );
982     i++;
983     bool isDiag = ( n == theNode1 || n == theNode2 );
984     if ( isDiag ) {
985       if ( iFirstDiag < 0 )
986         iFirstDiag = iNode;
987       else if ( iNode - iFirstDiag == 1 )
988         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
989     }
990     else if ( n == n4 ) {
991       return false; // tr1 and tr2 should not have all the same nodes
992     }
993     theQuadNodes[ iNode++ ] = n;
994   }
995   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996     theQuadNodes[ iNode ] = n4;
997
998   return true;
999 }
1000
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 //           with a quadrangle built on the same 4 nodes.
1005 //           Return false if proper faces not found
1006 //=======================================================================
1007
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009                                    const SMDS_MeshNode * theNode2)
1010 {
1011   myLastCreatedElems.Clear();
1012   myLastCreatedNodes.Clear();
1013
1014   MESSAGE( "::DeleteDiag()" );
1015
1016   const SMDS_MeshElement *tr1, *tr2;
1017   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1018     return false;
1019
1020   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021   if (!F1) return false;
1022   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023   if (!F2) return false;
1024   SMESHDS_Mesh * aMesh = GetMeshDS();
1025
1026   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1028
1029     const SMDS_MeshNode* aNodes [ 4 ];
1030     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1031       return false;
1032
1033     const SMDS_MeshElement* newElem = 0;
1034     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035     myLastCreatedElems.Append(newElem);
1036     AddToSameGroups( newElem, tr1, aMesh );
1037     int aShapeId = tr1->getshapeId();
1038     if ( aShapeId )
1039       {
1040         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1041       }
1042     aMesh->RemoveElement( tr1 );
1043     aMesh->RemoveElement( tr2 );
1044
1045     return true;
1046   }
1047
1048   // check case of quadratic faces
1049   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1050     return false;
1051   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1052     return false;
1053
1054   //       5
1055   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1057   //    |   / |
1058   //  7 +  +  + 6
1059   //    | /9  |
1060   //    |/    |
1061   //  4 +--+--+ 3
1062   //       8
1063
1064   vector< const SMDS_MeshNode* > N1;
1065   vector< const SMDS_MeshNode* > N2;
1066   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1067     return false;
1068   // now we receive following N1 and N2 (using numeration as above image)
1069   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1070   // i.e. first nodes from both arrays determ new diagonal
1071
1072   const SMDS_MeshNode* aNodes[8];
1073   aNodes[0] = N1[0];
1074   aNodes[1] = N1[1];
1075   aNodes[2] = N2[0];
1076   aNodes[3] = N2[1];
1077   aNodes[4] = N1[3];
1078   aNodes[5] = N2[5];
1079   aNodes[6] = N2[3];
1080   aNodes[7] = N1[5];
1081
1082   const SMDS_MeshElement* newElem = 0;
1083   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085   myLastCreatedElems.Append(newElem);
1086   AddToSameGroups( newElem, tr1, aMesh );
1087   int aShapeId = tr1->getshapeId();
1088   if ( aShapeId )
1089     {
1090       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1091     }
1092   aMesh->RemoveElement( tr1 );
1093   aMesh->RemoveElement( tr2 );
1094
1095   // remove middle node (9)
1096   GetMeshDS()->RemoveNode( N1[4] );
1097
1098   return true;
1099 }
1100
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose  : Reverse theElement orientation
1104 //=======================================================================
1105
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1107 {
1108   MESSAGE("Reorient");
1109   myLastCreatedElems.Clear();
1110   myLastCreatedNodes.Clear();
1111
1112   if (!theElem)
1113     return false;
1114   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115   if ( !it || !it->more() )
1116     return false;
1117
1118   const SMDSAbs_ElementType type = theElem->GetType();
1119   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1120     return false;
1121
1122   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1124   {
1125     const SMDS_VtkVolume* aPolyedre =
1126       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1127     if (!aPolyedre) {
1128       MESSAGE("Warning: bad volumic element");
1129       return false;
1130     }
1131     const int nbFaces = aPolyedre->NbFaces();
1132     vector<const SMDS_MeshNode *> poly_nodes;
1133     vector<int> quantities (nbFaces);
1134
1135     // reverse each face of the polyedre
1136     for (int iface = 1; iface <= nbFaces; iface++) {
1137       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138       quantities[iface - 1] = nbFaceNodes;
1139
1140       for (inode = nbFaceNodes; inode >= 1; inode--) {
1141         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142         poly_nodes.push_back(curNode);
1143       }
1144     }
1145     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1146   }
1147   else // other elements
1148   {
1149     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151     if ( interlace.empty() )
1152     {
1153       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1154     }
1155     else
1156     {
1157       SMDS_MeshCell::applyInterlace( interlace, nodes );
1158     }
1159     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1160   }
1161   return false;
1162 }
1163
1164 //================================================================================
1165 /*!
1166  * \brief Reorient faces.
1167  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168  * \param theDirection - desired direction of normal of \a theFace
1169  * \param theFace - one of \a theFaces that sould be oriented according to
1170  *        \a theDirection and whose orientation defines orientation of other faces
1171  * \return number of reoriented faces.
1172  */
1173 //================================================================================
1174
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1176                                   const gp_Dir&            theDirection,
1177                                   const SMDS_MeshElement * theFace)
1178 {
1179   int nbReori = 0;
1180   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1181
1182   if ( theFaces.empty() )
1183   {
1184     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185     while ( fIt->more() )
1186       theFaces.insert( theFaces.end(), fIt->next() );
1187   }
1188
1189   // orient theFace according to theDirection
1190   gp_XYZ normal;
1191   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192   if ( normal * theDirection.XYZ() < 0 )
1193     nbReori += Reorient( theFace );
1194
1195   // Orient other faces
1196
1197   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198   TIDSortedElemSet avoidSet;
1199   set< SMESH_TLink > checkedLinks;
1200   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1201
1202   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203     theFaces.erase( theFace );
1204   startFaces.insert( theFace );
1205
1206   int nodeInd1, nodeInd2;
1207   const SMDS_MeshElement*           otherFace;
1208   vector< const SMDS_MeshElement* > facesNearLink;
1209   vector< std::pair< int, int > >   nodeIndsOfFace;
1210
1211   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212   while ( !startFaces.empty() )
1213   {
1214     startFace = startFaces.begin();
1215     theFace = *startFace;
1216     startFaces.erase( startFace );
1217     if ( !visitedFaces.insert( theFace ).second )
1218       continue;
1219
1220     avoidSet.clear();
1221     avoidSet.insert(theFace);
1222
1223     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1224
1225     const int nbNodes = theFace->NbCornerNodes();
1226     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1227     {
1228       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229       linkIt_isNew = checkedLinks.insert( link );
1230       if ( !linkIt_isNew.second )
1231       {
1232         // link has already been checked and won't be encountered more
1233         // if the group (theFaces) is manifold
1234         //checkedLinks.erase( linkIt_isNew.first );
1235       }
1236       else
1237       {
1238         facesNearLink.clear();
1239         nodeIndsOfFace.clear();
1240         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1241                                                              theFaces, avoidSet,
1242                                                              &nodeInd1, &nodeInd2 )))
1243           if ( otherFace != theFace)
1244           {
1245             facesNearLink.push_back( otherFace );
1246             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247             avoidSet.insert( otherFace );
1248           }
1249         if ( facesNearLink.size() > 1 )
1250         {
1251           // NON-MANIFOLD mesh shell !
1252           // select a face most co-directed with theFace,
1253           // other faces won't be visited this time
1254           gp_XYZ NF, NOF;
1255           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256           double proj, maxProj = -1;
1257           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259             if (( proj = Abs( NF * NOF )) > maxProj ) {
1260               maxProj = proj;
1261               otherFace = facesNearLink[i];
1262               nodeInd1  = nodeIndsOfFace[i].first;
1263               nodeInd2  = nodeIndsOfFace[i].second;
1264             }
1265           }
1266           // not to visit rejected faces
1267           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269               visitedFaces.insert( facesNearLink[i] );
1270         }
1271         else if ( facesNearLink.size() == 1 )
1272         {
1273           otherFace = facesNearLink[0];
1274           nodeInd1  = nodeIndsOfFace.back().first;
1275           nodeInd2  = nodeIndsOfFace.back().second;
1276         }
1277         if ( otherFace && otherFace != theFace)
1278         {
1279           // link must be reverse in otherFace if orientation ot otherFace
1280           // is same as that of theFace
1281           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1282           {
1283             nbReori += Reorient( otherFace );
1284           }
1285           startFaces.insert( otherFace );
1286         }
1287       }
1288       std::swap( link.first, link.second ); // reverse the link
1289     }
1290   }
1291   return nbReori;
1292 }
1293
1294 //================================================================================
1295 /*!
1296  * \brief Reorient faces basing on orientation of adjacent volumes.
1297  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298  * \param theVolumes - reference volumes.
1299  * \param theOutsideNormal - to orient faces to have their normal
1300  *        pointing either \a outside or \a inside the adjacent volumes.
1301  * \return number of reoriented faces.
1302  */
1303 //================================================================================
1304
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306                                       TIDSortedElemSet & theVolumes,
1307                                       const bool         theOutsideNormal)
1308 {
1309   int nbReori = 0;
1310
1311   SMDS_ElemIteratorPtr faceIt;
1312   if ( theFaces.empty() )
1313     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1314   else
1315     faceIt = elemSetIterator( theFaces );
1316
1317   vector< const SMDS_MeshNode* > faceNodes;
1318   TIDSortedElemSet checkedVolumes;
1319   set< const SMDS_MeshNode* > faceNodesSet;
1320   SMDS_VolumeTool volumeTool;
1321
1322   while ( faceIt->more() ) // loop on given faces
1323   {
1324     const SMDS_MeshElement* face = faceIt->next();
1325     if ( face->GetType() != SMDSAbs_Face )
1326       continue;
1327
1328     const int nbCornersNodes = face->NbCornerNodes();
1329     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1330
1331     checkedVolumes.clear();
1332     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333     while ( vIt->more() )
1334     {
1335       const SMDS_MeshElement* volume = vIt->next();
1336
1337       if ( !checkedVolumes.insert( volume ).second )
1338         continue;
1339       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1340         continue;
1341
1342       // is volume adjacent?
1343       bool allNodesCommon = true;
1344       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346       if ( !allNodesCommon )
1347         continue;
1348
1349       // get nodes of a corresponding volume facet
1350       faceNodesSet.clear();
1351       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352       volumeTool.Set( volume );
1353       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354       if ( facetID < 0 ) continue;
1355       volumeTool.SetExternalNormal();
1356       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1357
1358       // compare order of faceNodes and facetNodes
1359       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1360       int iNN[2];
1361       for ( int i = 0; i < 2; ++i )
1362       {
1363         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365           if ( faceNodes[ iN ] == n )
1366           {
1367             iNN[ i ] = iN;
1368             break;
1369           }
1370       }
1371       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372       if ( isOutside != theOutsideNormal )
1373         nbReori += Reorient( face );
1374     }
1375   }  // loop on given faces
1376
1377   return nbReori;
1378 }
1379
1380 //=======================================================================
1381 //function : getBadRate
1382 //purpose  :
1383 //=======================================================================
1384
1385 static double getBadRate (const SMDS_MeshElement*               theElem,
1386                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1387 {
1388   SMESH::Controls::TSequenceOfXYZ P;
1389   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1390     return 1e100;
1391   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1393 }
1394
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose  : Cut quadrangles into triangles.
1398 //           theCrit is used to select a diagonal to cut
1399 //=======================================================================
1400
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1402                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1403 {
1404   myLastCreatedElems.Clear();
1405   myLastCreatedNodes.Clear();
1406
1407   if ( !theCrit.get() )
1408     return false;
1409
1410   SMESHDS_Mesh * aMesh = GetMeshDS();
1411
1412   Handle(Geom_Surface) surface;
1413   SMESH_MesherHelper   helper( *GetMesh() );
1414
1415   TIDSortedElemSet::iterator itElem;
1416   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1417   {
1418     const SMDS_MeshElement* elem = *itElem;
1419     if ( !elem || elem->GetType() != SMDSAbs_Face )
1420       continue;
1421     if ( elem->NbCornerNodes() != 4 )
1422       continue;
1423
1424     // retrieve element nodes
1425     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1426
1427     // compare two sets of possible triangles
1428     double aBadRate1, aBadRate2; // to what extent a set is bad
1429     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1432
1433     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1436
1437     const int aShapeId = FindShape( elem );
1438     const SMDS_MeshElement* newElem1 = 0;
1439     const SMDS_MeshElement* newElem2 = 0;
1440
1441     if ( !elem->IsQuadratic() ) // split liner quadrangle
1442     {
1443       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445       if ( aBadRate1 <= aBadRate2 ) {
1446         // tr1 + tr2 is better
1447         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1449       }
1450       else {
1451         // tr3 + tr4 is better
1452         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1454       }
1455     }
1456     else // split quadratic quadrangle
1457     {
1458       helper.SetIsQuadratic( true );
1459       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1460
1461       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462       if ( aNodes.size() == 9 )
1463       {
1464         helper.SetIsBiQuadratic( true );
1465         if ( aBadRate1 <= aBadRate2 )
1466           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1467         else
1468           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1469       }
1470       // create a new element
1471       if ( aBadRate1 <= aBadRate2 ) {
1472         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1474       }
1475       else {
1476         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1478       }
1479     } // quadratic case
1480
1481     // care of a new element
1482
1483     myLastCreatedElems.Append(newElem1);
1484     myLastCreatedElems.Append(newElem2);
1485     AddToSameGroups( newElem1, elem, aMesh );
1486     AddToSameGroups( newElem2, elem, aMesh );
1487
1488     // put a new triangle on the same shape
1489     if ( aShapeId )
1490       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1492
1493     aMesh->RemoveElement( elem );
1494   }
1495   return true;
1496 }
1497
1498 //=======================================================================
1499 /*!
1500  * \brief Split each of given quadrangles into 4 triangles.
1501  * \param theElems - The faces to be splitted. If empty all faces are split.
1502  */
1503 //=======================================================================
1504
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1506 {
1507   myLastCreatedElems.Clear();
1508   myLastCreatedNodes.Clear();
1509
1510   SMESH_MesherHelper helper( *GetMesh() );
1511   helper.SetElementsOnShape( true );
1512
1513   SMDS_ElemIteratorPtr faceIt;
1514   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515   else                    faceIt = elemSetIterator( theElems );
1516
1517   bool   checkUV;
1518   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1519   gp_XYZ xyz[9];
1520   vector< const SMDS_MeshNode* > nodes;
1521   SMESHDS_SubMesh*               subMeshDS;
1522   TopoDS_Face                    F;
1523   Handle(Geom_Surface)           surface;
1524   TopLoc_Location                loc;
1525
1526   while ( faceIt->more() )
1527   {
1528     const SMDS_MeshElement* quad = faceIt->next();
1529     if ( !quad || quad->NbCornerNodes() != 4 )
1530       continue;
1531
1532     // get a surface the quad is on
1533
1534     if ( quad->getshapeId() < 1 )
1535     {
1536       F.Nullify();
1537       helper.SetSubShape( 0 );
1538       subMeshDS = 0;
1539     }
1540     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1541     {
1542       helper.SetSubShape( quad->getshapeId() );
1543       if ( !helper.GetSubShape().IsNull() &&
1544            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1545       {
1546         F = TopoDS::Face( helper.GetSubShape() );
1547         surface = BRep_Tool::Surface( F, loc );
1548         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1549       }
1550       else
1551       {
1552         helper.SetSubShape( 0 );
1553         subMeshDS = 0;
1554       }
1555     }
1556
1557     // create a central node
1558
1559     const SMDS_MeshNode* nCentral;
1560     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1561
1562     if ( nodes.size() == 9 )
1563     {
1564       nCentral = nodes.back();
1565     }
1566     else
1567     {
1568       size_t iN = 0;
1569       if ( F.IsNull() )
1570       {
1571         for ( ; iN < nodes.size(); ++iN )
1572           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1573
1574         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1576
1577         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578                                    xyz[0], xyz[1], xyz[2], xyz[3],
1579                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1580       }
1581       else
1582       {
1583         for ( ; iN < nodes.size(); ++iN )
1584           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1585
1586         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1588
1589         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590                                   uv[0], uv[1], uv[2], uv[3],
1591                                   uv[4], uv[5], uv[6], uv[7] );
1592
1593         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1594         xyz[ 8 ] = p.XYZ();
1595       }
1596
1597       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598                                  uv[8].X(), uv[8].Y() );
1599       myLastCreatedNodes.Append( nCentral );
1600     }
1601
1602     // create 4 triangles
1603
1604     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1605     
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     for ( int i = 0; i < 4; ++i )
1612     {
1613       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1614                                                nodes[(i+1)%4],
1615                                                nCentral );
1616       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617       myLastCreatedElems.Append( tria );
1618     }
1619   }
1620 }
1621
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose  : Find better diagonal for cutting.
1625 //=======================================================================
1626
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1628                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1629 {
1630   myLastCreatedElems.Clear();
1631   myLastCreatedNodes.Clear();
1632
1633   if (!theCrit.get())
1634     return -1;
1635
1636   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1637     return -1;
1638
1639   if( theQuad->NbNodes()==4 ||
1640       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1641
1642     // retrieve element nodes
1643     const SMDS_MeshNode* aNodes [4];
1644     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1645     int i = 0;
1646     //while (itN->more())
1647     while (i<4) {
1648       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1649     }
1650     // compare two sets of possible triangles
1651     double aBadRate1, aBadRate2; // to what extent a set is bad
1652     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1655
1656     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662       return 1; // diagonal 1-3
1663
1664     return 2; // diagonal 2-4
1665   }
1666   return -1;
1667 }
1668
1669 namespace
1670 {
1671   // Methods of splitting volumes into tetra
1672
1673   const int theHexTo5_1[5*4+1] =
1674     {
1675       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1676     };
1677   const int theHexTo5_2[5*4+1] =
1678     {
1679       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1680     };
1681   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1682
1683   const int theHexTo6_1[6*4+1] =
1684     {
1685       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
1686     };
1687   const int theHexTo6_2[6*4+1] =
1688     {
1689       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
1690     };
1691   const int theHexTo6_3[6*4+1] =
1692     {
1693       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
1694     };
1695   const int theHexTo6_4[6*4+1] =
1696     {
1697       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
1698     };
1699   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1700
1701   const int thePyraTo2_1[2*4+1] =
1702     {
1703       0, 1, 2, 4,    0, 2, 3, 4,   -1
1704     };
1705   const int thePyraTo2_2[2*4+1] =
1706     {
1707       1, 2, 3, 4,    1, 3, 0, 4,   -1
1708     };
1709   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1710
1711   const int thePentaTo3_1[3*4+1] =
1712     {
1713       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1714     };
1715   const int thePentaTo3_2[3*4+1] =
1716     {
1717       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1718     };
1719   const int thePentaTo3_3[3*4+1] =
1720     {
1721       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1722     };
1723   const int thePentaTo3_4[3*4+1] =
1724     {
1725       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1726     };
1727   const int thePentaTo3_5[3*4+1] =
1728     {
1729       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1730     };
1731   const int thePentaTo3_6[3*4+1] =
1732     {
1733       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1734     };
1735   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1737
1738   // Methods of splitting hexahedron into prisms
1739
1740   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1741     {
1742       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
1743     };
1744   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1745     {
1746       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
1747     };
1748   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1749     {
1750       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
1751     };
1752
1753   const int theHexTo2Prisms_BT_1[6*2+1] =
1754     {
1755       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1756     };
1757   const int theHexTo2Prisms_BT_2[6*2+1] =
1758     {
1759       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1760     };
1761   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1762
1763   const int theHexTo2Prisms_LR_1[6*2+1] =
1764     {
1765       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1766     };
1767   const int theHexTo2Prisms_LR_2[6*2+1] =
1768     {
1769       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1770     };
1771   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1772
1773   const int theHexTo2Prisms_FB_1[6*2+1] =
1774     {
1775       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1776     };
1777   const int theHexTo2Prisms_FB_2[6*2+1] =
1778     {
1779       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1780     };
1781   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1782
1783
1784   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1785   {
1786     int _n1, _n2, _n3;
1787     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1790                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1791   };
1792   struct TSplitMethod
1793   {
1794     int        _nbSplits;
1795     int        _nbCorners;
1796     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1798     bool       _ownConn;      //!< to delete _connectivity in destructor
1799     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1800
1801     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804     bool hasFacet( const TTriangleFacet& facet ) const
1805     {
1806       if ( _nbCorners == 4 )
1807       {
1808         const int* tetConn = _connectivity;
1809         for ( ; tetConn[0] >= 0; tetConn += 4 )
1810           if (( facet.contains( tetConn[0] ) +
1811                 facet.contains( tetConn[1] ) +
1812                 facet.contains( tetConn[2] ) +
1813                 facet.contains( tetConn[3] )) == 3 )
1814             return true;
1815       }
1816       else // prism, _nbCorners == 6
1817       {
1818         const int* prismConn = _connectivity;
1819         for ( ; prismConn[0] >= 0; prismConn += 6 )
1820         {
1821           if (( facet.contains( prismConn[0] ) &&
1822                 facet.contains( prismConn[1] ) &&
1823                 facet.contains( prismConn[2] ))
1824               ||
1825               ( facet.contains( prismConn[3] ) &&
1826                 facet.contains( prismConn[4] ) &&
1827                 facet.contains( prismConn[5] )))
1828             return true;
1829         }
1830       }
1831       return false;
1832     }
1833   };
1834
1835   //=======================================================================
1836   /*!
1837    * \brief return TSplitMethod for the given element to split into tetrahedra
1838    */
1839   //=======================================================================
1840
1841   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1842   {
1843     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1844
1845     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846     // an edge and a face barycenter; tertaherdons are based on triangles and
1847     // a volume barycenter
1848     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1849
1850     // Find out how adjacent volumes are split
1851
1852     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1855     {
1856       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858       if ( nbNodes < 4 ) continue;
1859
1860       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861       const int* nInd = vol.GetFaceNodesIndices( iF );
1862       if ( nbNodes == 4 )
1863       {
1864         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1868       }
1869       else
1870       {
1871         int iCom = 0; // common node of triangle faces to split into
1872         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1873         {
1874           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1875                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1876                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1879                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1881           {
1882             triaSplits.push_back( t012 );
1883             triaSplits.push_back( t023 );
1884             break;
1885           }
1886         }
1887       }
1888       if ( !triaSplits.empty() )
1889         hasAdjacentSplits = true;
1890     }
1891
1892     // Among variants of split method select one compliant with adjacent volumes
1893
1894     TSplitMethod method;
1895     if ( !vol.Element()->IsPoly() && !is24TetMode )
1896     {
1897       int nbVariants = 2, nbTet = 0;
1898       const int** connVariants = 0;
1899       switch ( vol.Element()->GetEntityType() )
1900       {
1901       case SMDSEntity_Hexa:
1902       case SMDSEntity_Quad_Hexa:
1903       case SMDSEntity_TriQuad_Hexa:
1904         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905           connVariants = theHexTo5, nbTet = 5;
1906         else
1907           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1908         break;
1909       case SMDSEntity_Pyramid:
1910       case SMDSEntity_Quad_Pyramid:
1911         connVariants = thePyraTo2;  nbTet = 2;
1912         break;
1913       case SMDSEntity_Penta:
1914       case SMDSEntity_Quad_Penta:
1915         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1916         break;
1917       default:
1918         nbVariants = 0;
1919       }
1920       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1921       {
1922         // check method compliancy with adjacent tetras,
1923         // all found splits must be among facets of tetras described by this method
1924         method = TSplitMethod( nbTet, connVariants[variant] );
1925         if ( hasAdjacentSplits && method._nbSplits > 0 )
1926         {
1927           bool facetCreated = true;
1928           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1929           {
1930             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932               facetCreated = method.hasFacet( *facet );
1933           }
1934           if ( !facetCreated )
1935             method = TSplitMethod(0); // incompatible method
1936         }
1937       }
1938     }
1939     if ( method._nbSplits < 1 )
1940     {
1941       // No standard method is applicable, use a generic solution:
1942       // each facet of a volume is split into triangles and
1943       // each of triangles and a volume barycenter form a tetrahedron.
1944
1945       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1946
1947       int* connectivity = new int[ maxTetConnSize + 1 ];
1948       method._connectivity = connectivity;
1949       method._ownConn = true;
1950       method._baryNode = !isHex27; // to create central node or not
1951
1952       int connSize = 0;
1953       int baryCenInd = vol.NbNodes() - int( isHex27 );
1954       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1955       {
1956         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957         const int*   nInd = vol.GetFaceNodesIndices( iF );
1958         // find common node of triangle facets of tetra to create
1959         int iCommon = 0; // index in linear numeration
1960         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961         if ( !triaSplits.empty() )
1962         {
1963           // by found facets
1964           const TTriangleFacet* facet = &triaSplits.front();
1965           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1968               break;
1969         }
1970         else if ( nbNodes > 3 && !is24TetMode )
1971         {
1972           // find the best method of splitting into triangles by aspect ratio
1973           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974           map< double, int > badness2iCommon;
1975           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1978           {
1979             double badness = 0;
1980             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1981             {
1982               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1983                                       nodes[ iQ*((iLast-1)%nbNodes)],
1984                                       nodes[ iQ*((iLast  )%nbNodes)]);
1985               badness += getBadRate( &tria, aspectRatio );
1986             }
1987             badness2iCommon.insert( make_pair( badness, iCommon ));
1988           }
1989           // use iCommon with lowest badness
1990           iCommon = badness2iCommon.begin()->second;
1991         }
1992         if ( iCommon >= nbNodes )
1993           iCommon = 0; // something wrong
1994
1995         // fill connectivity of tetrahedra based on a current face
1996         int nbTet = nbNodes - 2;
1997         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1998         {
1999           int faceBaryCenInd;
2000           if ( isHex27 )
2001           {
2002             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2004           }
2005           else
2006           {
2007             method._faceBaryNode[ iF ] = 0;
2008             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2009           }
2010           nbTet = nbNodes;
2011           for ( int i = 0; i < nbTet; ++i )
2012           {
2013             int i1 = i, i2 = (i+1) % nbNodes;
2014             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017             connectivity[ connSize++ ] = faceBaryCenInd;
2018             connectivity[ connSize++ ] = baryCenInd;
2019           }
2020         }
2021         else
2022         {
2023           for ( int i = 0; i < nbTet; ++i )
2024           {
2025             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030             connectivity[ connSize++ ] = baryCenInd;
2031           }
2032         }
2033         method._nbSplits += nbTet;
2034
2035       } // loop on volume faces
2036
2037       connectivity[ connSize++ ] = -1;
2038
2039     } // end of generic solution
2040
2041     return method;
2042   }
2043   //=======================================================================
2044   /*!
2045    * \brief return TSplitMethod to split haxhedron into prisms
2046    */
2047   //=======================================================================
2048
2049   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050                                     const int        methodFlags,
2051                                     const int        facetToSplit)
2052   {
2053     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2054     // B, T, L, B, R, F
2055     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2056
2057     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2058     {
2059       static TSplitMethod to4methods[4]; // order BT, LR, FB
2060       if ( to4methods[iF]._nbSplits == 0 )
2061       {
2062         switch ( iF ) {
2063         case 0:
2064           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2067           break;
2068         case 1:
2069           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2072           break;
2073         case 2:
2074           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2077           break;
2078         default: return to4methods[3];
2079         }
2080         to4methods[iF]._nbSplits  = 4;
2081         to4methods[iF]._nbCorners = 6;
2082       }
2083       return to4methods[iF];
2084     }
2085     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2086
2087     TSplitMethod method;
2088
2089     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2090
2091     const int nbVariants = 2, nbSplits = 2;
2092     const int** connVariants = 0;
2093     switch ( iF ) {
2094     case 0: connVariants = theHexTo2Prisms_BT; break;
2095     case 1: connVariants = theHexTo2Prisms_LR; break;
2096     case 2: connVariants = theHexTo2Prisms_FB; break;
2097     default: return method;
2098     }
2099
2100     // look for prisms adjacent via facetToSplit and an opposite one
2101     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2102     {
2103       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105       if ( nbNodes != 4 ) return method;
2106
2107       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110       TTriangleFacet* t;
2111       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112         t = &t012;
2113       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t123;
2115       else
2116         continue;
2117
2118       // there are adjacent prism
2119       for ( int variant = 0; variant < nbVariants; ++variant )
2120       {
2121         // check method compliancy with adjacent prisms,
2122         // the found prism facets must be among facets of prisms described by current method
2123         method._nbSplits     = nbSplits;
2124         method._nbCorners    = 6;
2125         method._connectivity = connVariants[ variant ];
2126         if ( method.hasFacet( *t ))
2127           return method;
2128       }
2129     }
2130
2131     // No adjacent prisms. Select a variant with a best aspect ratio.
2132
2133     double badness[2] = { 0, 0 };
2134     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135     const SMDS_MeshNode** nodes = vol.GetNodes();
2136     for ( int variant = 0; variant < nbVariants; ++variant )
2137       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2138       {
2139         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2141
2142         method._connectivity = connVariants[ variant ];
2143         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2146
2147         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2148                                 nodes[ t->_n2 ],
2149                                 nodes[ t->_n3 ] );
2150         badness[ variant ] += getBadRate( &tria, aspectRatio );
2151       }
2152     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2153
2154     method._nbSplits     = nbSplits;
2155     method._nbCorners    = 6;
2156     method._connectivity = connVariants[ iBetter ];
2157
2158     return method;
2159   }
2160
2161   //================================================================================
2162   /*!
2163    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2164    */
2165   //================================================================================
2166
2167   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2168                                        const SMDSAbs_GeometryType geom ) const
2169   {
2170     // find the tetrahedron including the three nodes of facet
2171     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175     while ( volIt1->more() )
2176     {
2177       const SMDS_MeshElement* v = volIt1->next();
2178       if ( v->GetGeomType() != geom )
2179         continue;
2180       const int lastCornerInd = v->NbCornerNodes() - 1;
2181       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182         continue; // medium node not allowed
2183       const int ind2 = v->GetNodeIndex( n2 );
2184       if ( ind2 < 0 || lastCornerInd < ind2 )
2185         continue;
2186       const int ind3 = v->GetNodeIndex( n3 );
2187       if ( ind3 < 0 || lastCornerInd < ind3 )
2188         continue;
2189       return true;
2190     }
2191     return false;
2192   }
2193
2194   //=======================================================================
2195   /*!
2196    * \brief A key of a face of volume
2197    */
2198   //=======================================================================
2199
2200   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2201   {
2202     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2203     {
2204       TIDSortedNodeSet sortedNodes;
2205       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206       int nbNodes = vol.NbFaceNodes( iF );
2207       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208       for ( int i = 0; i < nbNodes; i += iQ )
2209         sortedNodes.insert( fNodes[i] );
2210       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211       first.first   = (*(n++))->GetID();
2212       first.second  = (*(n++))->GetID();
2213       second.first  = (*(n++))->GetID();
2214       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2215     }
2216   };
2217 } // namespace
2218
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose  : Split volume elements into tetrahedra or prisms.
2222 //           If facet ID < 0, element is split into tetrahedra,
2223 //           else a hexahedron is split into prisms so that the given facet is
2224 //           split into triangles
2225 //=======================================================================
2226
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228                                      const int            theMethodFlags)
2229 {
2230   SMDS_VolumeTool    volTool;
2231   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232   fHelper.ToFixNodeParameters( true );
2233
2234   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2236
2237   SMESH_SequenceOfElemPtr newNodes, newElems;
2238
2239   // map face of volume to it's baricenrtic node
2240   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2241   double bc[3];
2242   vector<const SMDS_MeshElement* > splitVols;
2243
2244   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245   for ( ; elem2facet != theElems.end(); ++elem2facet )
2246   {
2247     const SMDS_MeshElement* elem = elem2facet->first;
2248     const int       facetToSplit = elem2facet->second;
2249     if ( elem->GetType() != SMDSAbs_Volume )
2250       continue;
2251     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2253       continue;
2254
2255     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256
2257     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2258                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2259                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260     if ( splitMethod._nbSplits < 1 ) continue;
2261
2262     // find submesh to add new tetras to
2263     if ( !subMesh || !subMesh->Contains( elem ))
2264     {
2265       int shapeID = FindShape( elem );
2266       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267       subMesh = GetMeshDS()->MeshElements( shapeID );
2268     }
2269     int iQ;
2270     if ( elem->IsQuadratic() )
2271     {
2272       iQ = 2;
2273       // add quadratic links to the helper
2274       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275       {
2276         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278         for ( int iN = 0; iN < nbN; iN += iQ )
2279           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280       }
2281       helper.SetIsQuadratic( true );
2282     }
2283     else
2284     {
2285       iQ = 1;
2286       helper.SetIsQuadratic( false );
2287     }
2288     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289                                         volTool.GetNodes() + elem->NbNodes() );
2290     helper.SetElementsOnShape( true );
2291     if ( splitMethod._baryNode )
2292     {
2293       // make a node at barycenter
2294       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296       nodes.push_back( gcNode );
2297       newNodes.Append( gcNode );
2298     }
2299     if ( !splitMethod._faceBaryNode.empty() )
2300     {
2301       // make or find baricentric nodes of faces
2302       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304       {
2305         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306           volFace2BaryNode.insert
2307           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2308         if ( !f_n->second )
2309         {
2310           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312         }
2313         nodes.push_back( iF_n->second = f_n->second );
2314       }
2315     }
2316
2317     // make new volumes
2318     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319     const int* volConn = splitMethod._connectivity;
2320     if ( splitMethod._nbCorners == 4 ) // tetra
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     else // prisms
2327       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329                                                             nodes[ volConn[1] ],
2330                                                             nodes[ volConn[2] ],
2331                                                             nodes[ volConn[3] ],
2332                                                             nodes[ volConn[4] ],
2333                                                             nodes[ volConn[5] ]));
2334
2335     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336
2337     // Split faces on sides of the split volume
2338
2339     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341     {
2342       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343       if ( nbNodes < 4 ) continue;
2344
2345       // find an existing face
2346       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349                                                                        /*noMedium=*/false))
2350       {
2351         // make triangles
2352         helper.SetElementsOnShape( false );
2353         vector< const SMDS_MeshElement* > triangles;
2354
2355         // find submesh to add new triangles in
2356         if ( !fSubMesh || !fSubMesh->Contains( face ))
2357         {
2358           int shapeID = FindShape( face );
2359           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360         }
2361         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362         if ( iF_n != splitMethod._faceBaryNode.end() )
2363         {
2364           const SMDS_MeshNode *baryNode = iF_n->second;
2365           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366           {
2367             const SMDS_MeshNode* n1 = fNodes[iN];
2368             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369             const SMDS_MeshNode *n3 = baryNode;
2370             if ( !volTool.IsFaceExternal( iF ))
2371               swap( n2, n3 );
2372             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373           }
2374           if ( fSubMesh ) // update position of the bary node on geometry
2375           {
2376             if ( subMesh )
2377               subMesh->RemoveNode( baryNode, false );
2378             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381             {
2382               fHelper.SetSubShape( s );
2383               gp_XY uv( 1e100, 1e100 );
2384               double distXYZ[4];
2385               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2387                    uv.X() < 1e100 )
2388               {
2389                 // node is too far from the surface
2390                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393               }
2394             }
2395           }
2396         }
2397         else
2398         {
2399           // among possible triangles create ones discribed by split method
2400           const int* nInd = volTool.GetFaceNodesIndices( iF );
2401           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402           int iCom = 0; // common node of triangle faces to split into
2403           list< TTriangleFacet > facets;
2404           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405           {
2406             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2407                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2408                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413             {
2414               facets.push_back( t012 );
2415               facets.push_back( t023 );
2416               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2418                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2419                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2420               break;
2421             }
2422           }
2423           list< TTriangleFacet >::iterator facet = facets.begin();
2424           if ( facet == facets.end() )
2425             break;
2426           for ( ; facet != facets.end(); ++facet )
2427           {
2428             if ( !volTool.IsFaceExternal( iF ))
2429               swap( facet->_n2, facet->_n3 );
2430             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431                                                  volNodes[ facet->_n2 ],
2432                                                  volNodes[ facet->_n3 ]));
2433           }
2434         }
2435         for ( int i = 0; i < triangles.size(); ++i )
2436         {
2437           if ( !triangles[i] ) continue;
2438           if ( fSubMesh )
2439             fSubMesh->AddElement( triangles[i]);
2440           newElems.Append( triangles[i] );
2441         }
2442         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444
2445       } // while a face based on facet nodes exists
2446     } // loop on volume faces to split them into triangles
2447
2448     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449
2450     if ( geomType == SMDSEntity_TriQuad_Hexa )
2451     {
2452       // remove medium nodes that could become free
2453       for ( int i = 20; i < volTool.NbNodes(); ++i )
2454         if ( volNodes[i]->NbInverseElements() == 0 )
2455           GetMeshDS()->RemoveNode( volNodes[i] );
2456     }
2457   } // loop on volumes to split
2458   
2459   myLastCreatedNodes = newNodes;
2460   myLastCreatedElems = newElems;
2461 }
2462
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose  : For hexahedra that will be split into prisms, finds facets to
2466 //           split into triangles. Only hexahedra adjacent to the one closest
2467 //           to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in]     theFacetNormal - facet normal
2470 //param [out]    theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2472
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474                                              const gp_Ax1&     theFacetNormal,
2475                                              TFacetOfElem &    theFacets)
2476 {
2477   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478
2479   // Find a hexa closest to the location of theFacetNormal
2480
2481   const SMDS_MeshElement* startHex;
2482   {
2483     // get SMDS_ElemIteratorPtr on theHexas
2484     typedef const SMDS_MeshElement*                                      TValue;
2485     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2486     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2487     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2488     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490       ( new TElemSetIter( theHexas.begin(),
2491                           theHexas.end(),
2492                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493
2494     SMESH_ElementSearcher* searcher =
2495       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496
2497     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2498
2499     delete searcher;
2500
2501     if ( !startHex )
2502       throw SALOME_Exception( THIS_METHOD "startHex not found");
2503   }
2504
2505   // Select a facet of startHex by theFacetNormal
2506
2507   SMDS_VolumeTool vTool( startHex );
2508   double norm[3], dot, maxDot = 0;
2509   int facetID = -1;
2510   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512     {
2513       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2514       if ( dot > maxDot )
2515       {
2516         facetID = iF;
2517         maxDot = dot;
2518       }
2519     }
2520   if ( facetID < 0 )
2521     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522
2523   // Fill theFacets starting from facetID of startHex
2524
2525   // facets used for seach of volumes adjacent to already treated ones
2526   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2528   TFacetMap facetsToCheck;
2529
2530   set<const SMDS_MeshNode*> facetNodes;
2531   const SMDS_MeshElement*   curHex;
2532
2533   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2534
2535   while ( startHex )
2536   {
2537     // move in two directions from startHex via facetID
2538     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2539     {
2540       curHex       = startHex;
2541       int curFacet = facetID;
2542       if ( is2nd ) // do not treat startHex twice
2543       {
2544         vTool.Set( curHex );
2545         if ( vTool.IsFreeFace( curFacet, &curHex ))
2546         {
2547           curHex = 0;
2548         }
2549         else
2550         {
2551           vTool.GetFaceNodes( curFacet, facetNodes );
2552           vTool.Set( curHex );
2553           curFacet = vTool.GetFaceIndex( facetNodes );
2554         }
2555       }
2556       while ( curHex )
2557       {
2558         // store a facet to split
2559         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560         {
2561           theFacets.insert( make_pair( curHex, -1 ));
2562           break;
2563         }
2564         if ( !allHex && !theHexas.count( curHex ))
2565           break;
2566
2567         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568           theFacets.insert( make_pair( curHex, curFacet ));
2569         if ( !facetIt2isNew.second )
2570           break;
2571
2572         // remember not-to-split facets in facetsToCheck
2573         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575         {
2576           if ( iF == curFacet && iF == oppFacet )
2577             continue;
2578           TVolumeFaceKey facetKey ( vTool, iF );
2579           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2580           pair< TFacetMap::iterator, bool > it2isnew =
2581             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582           if ( !it2isnew.second )
2583             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584         }
2585         // pass to a volume adjacent via oppFacet
2586         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2587         {
2588           curHex = 0;
2589         }
2590         else
2591         {
2592           // get a new curFacet
2593           vTool.GetFaceNodes( oppFacet, facetNodes );
2594           vTool.Set( curHex );
2595           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2596         }
2597       }
2598     } // move in two directions from startHex via facetID
2599
2600     // Find a new startHex by facetsToCheck
2601
2602     startHex = 0;
2603     facetID  = -1;
2604     TFacetMap::iterator fIt = facetsToCheck.begin();
2605     while ( !startHex && fIt != facetsToCheck.end() )
2606     {
2607       const TElemFacets&  elemFacets = fIt->second;
2608       const SMDS_MeshElement*    hex = elemFacets.first->first;
2609       int                 splitFacet = elemFacets.first->second;
2610       int               lateralFacet = elemFacets.second;
2611       facetsToCheck.erase( fIt );
2612       fIt = facetsToCheck.begin();
2613
2614       vTool.Set( hex );
2615       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2616            curHex->GetGeomType() != SMDSGeom_HEXA )
2617         continue;
2618       if ( !allHex && !theHexas.count( curHex ))
2619         continue;
2620
2621       startHex = curHex;
2622
2623       // find a facet of startHex to split
2624
2625       set<const SMDS_MeshNode*> lateralNodes;
2626       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627       vTool.GetFaceNodes( splitFacet,   facetNodes );
2628       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629       vTool.Set( startHex );
2630       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631
2632       // look for a facet of startHex having common nodes with facetNodes
2633       // but not lateralFacet
2634       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635       {
2636         if ( iF == lateralFacet )
2637           continue;
2638         int nbCommonNodes = 0;
2639         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641           nbCommonNodes += facetNodes.count( nn[ iN ]);
2642
2643         if ( nbCommonNodes >= 2 )
2644         {
2645           facetID = iF;
2646           break;
2647         }
2648       }
2649       if ( facetID < 0 )
2650         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651     }
2652   } //   while ( startHex )
2653
2654   return;
2655 }
2656
2657 namespace
2658 {
2659   //================================================================================
2660   /*!
2661    * \brief Selects nodes of several elements according to a given interlace
2662    *  \param [in] srcNodes - nodes to select from
2663    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664    *  \param [in] interlace - indices of nodes for all elements
2665    *  \param [in] nbElems - nb of elements
2666    *  \param [in] nbNodes - nb of nodes in each element
2667    *  \param [in] mesh - the mesh
2668    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2669    *  \param [in] type - type of elements to look for
2670    */
2671   //================================================================================
2672
2673   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2675                     const int*                            interlace,
2676                     const int                             nbElems,
2677                     const int                             nbNodes,
2678                     SMESHDS_Mesh*                         mesh = 0,
2679                     list< const SMDS_MeshElement* >*      elemQueue=0,
2680                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2681   {
2682     for ( int iE = 0; iE < nbElems; ++iE )
2683     {
2684       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685       const int*                         select = & interlace[iE*nbNodes];
2686       elemNodes.resize( nbNodes );
2687       for ( int iN = 0; iN < nbNodes; ++iN )
2688         elemNodes[iN] = srcNodes[ select[ iN ]];
2689     }
2690     const SMDS_MeshElement* e;
2691     if ( elemQueue )
2692       for ( int iE = 0; iE < nbElems; ++iE )
2693         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694           elemQueue->push_back( e );
2695   }
2696 }
2697
2698 //=======================================================================
2699 /*
2700  * Split bi-quadratic elements into linear ones without creation of additional nodes
2701  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2702  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2705  *   will be split in order to keep the mesh conformal.
2706  *  \param elems - elements to split
2707  */
2708 //=======================================================================
2709
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2711 {
2712   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713   vector<const SMDS_MeshElement* > splitElems;
2714   list< const SMDS_MeshElement* > elemQueue;
2715   list< const SMDS_MeshElement* >::iterator elemIt;
2716
2717   SMESHDS_Mesh * mesh = GetMeshDS();
2718   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719   int nbElems, nbNodes;
2720
2721   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2723   {
2724     elemQueue.clear();
2725     elemQueue.push_back( *elemSetIt );
2726     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2727     {
2728       const SMDS_MeshElement* elem = *elemIt;
2729       switch( elem->GetEntityType() )
2730       {
2731       case SMDSEntity_TriQuad_Hexa: // HEX27
2732       {
2733         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734         nbElems  = nbNodes = 8;
2735         elemType = & hexaType;
2736
2737         // get nodes for new elements
2738         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2739                                  { 1,9,20,8,    17,22,26,21 },
2740                                  { 2,10,20,9,   18,23,26,22 },
2741                                  { 3,11,20,10,  19,24,26,23 },
2742                                  { 16,21,26,24, 4,12,25,15  },
2743                                  { 17,22,26,21, 5,13,25,12  },
2744                                  { 18,23,26,22, 6,14,25,13  },
2745                                  { 19,24,26,23, 7,15,25,14  }};
2746         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2747
2748         // add boundary faces to elemQueue
2749         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2750                                  { 4,5,6,7, 12,13,14,15, 25 },
2751                                  { 0,1,5,4, 8,17,12,16,  21 },
2752                                  { 1,2,6,5, 9,18,13,17,  22 },
2753                                  { 2,3,7,6, 10,19,14,18, 23 },
2754                                  { 3,0,4,7, 11,16,15,19, 24 }};
2755         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2756
2757         // add boundary segments to elemQueue
2758         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Triangle: // TRIA7
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 3;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2781       {
2782         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2783         nbElems = 4;
2784         nbNodes = 4;
2785         elemType = & quadType;
2786
2787         // get nodes for new elements
2788         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2790
2791         // add boundary segments to elemQueue
2792         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2794         break;
2795       }
2796       case SMDSEntity_Quad_Edge:
2797       {
2798         if ( elemIt == elemQueue.begin() )
2799           continue; // an elem is in theElems
2800         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2801         nbElems = 2;
2802         nbNodes = 2;
2803         elemType = & segType;
2804
2805         // get nodes for new elements
2806         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2808         break;
2809       }
2810       default: continue;
2811       } // switch( elem->GetEntityType() )
2812
2813       // Create new elements
2814
2815       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2816
2817       splitElems.clear();
2818
2819       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822       //elemType->SetID( -1 );
2823
2824       for ( int iE = 0; iE < nbElems; ++iE )
2825         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2826
2827
2828       ReplaceElemInGroups( elem, splitElems, mesh );
2829
2830       if ( subMesh )
2831         for ( size_t i = 0; i < splitElems.size(); ++i )
2832           subMesh->AddElement( splitElems[i] );
2833     }
2834   }
2835 }
2836
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2841
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843                                         const SMDS_MeshElement* elemInGroups,
2844                                         SMESHDS_Mesh *          aMesh)
2845 {
2846   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847   if (!groups.empty()) {
2848     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849     for ( ; grIt != groups.end(); grIt++ ) {
2850       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851       if ( group && group->Contains( elemInGroups ))
2852         group->SMDSGroup().Add( elemToAdd );
2853     }
2854   }
2855 }
2856
2857
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863                                              SMESHDS_Mesh *          aMesh)
2864 {
2865   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866   if (!groups.empty())
2867   {
2868     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869     for (; GrIt != groups.end(); GrIt++)
2870     {
2871       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872       if (!grp || grp->IsEmpty()) continue;
2873       grp->SMDSGroup().Remove(removeelem);
2874     }
2875   }
2876 }
2877
2878 //================================================================================
2879 /*!
2880  * \brief Replace elemToRm by elemToAdd in the all groups
2881  */
2882 //================================================================================
2883
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885                                             const SMDS_MeshElement* elemToAdd,
2886                                             SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty()) {
2890     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891     for ( ; grIt != groups.end(); grIt++ ) {
2892       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894         group->SMDSGroup().Add( elemToAdd );
2895     }
2896   }
2897 }
2898
2899 //================================================================================
2900 /*!
2901  * \brief Replace elemToRm by elemToAdd in the all groups
2902  */
2903 //================================================================================
2904
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2906                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2907                                             SMESHDS_Mesh *                         aMesh)
2908 {
2909   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910   if (!groups.empty())
2911   {
2912     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913     for ( ; grIt != groups.end(); grIt++ ) {
2914       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916         for ( int i = 0; i < elemToAdd.size(); ++i )
2917           group->SMDSGroup().Add( elemToAdd[ i ] );
2918     }
2919   }
2920 }
2921
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose  : Cut quadrangles into triangles.
2925 //           theCrit is used to select a diagonal to cut
2926 //=======================================================================
2927
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929                                   const bool         the13Diag)
2930 {
2931   myLastCreatedElems.Clear();
2932   myLastCreatedNodes.Clear();
2933
2934   MESSAGE( "::QuadToTri()" );
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2943     const SMDS_MeshElement* elem = *itElem;
2944     if ( !elem || elem->GetType() != SMDSAbs_Face )
2945       continue;
2946     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2947     if(!isquad) continue;
2948
2949     if(elem->NbNodes()==4) {
2950       // retrieve element nodes
2951       const SMDS_MeshNode* aNodes [4];
2952       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953       int i = 0;
2954       while ( itN->more() )
2955         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956
2957       int aShapeId = FindShape( elem );
2958       const SMDS_MeshElement* newElem1 = 0;
2959       const SMDS_MeshElement* newElem2 = 0;
2960       if ( the13Diag ) {
2961         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2963       }
2964       else {
2965         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967       }
2968       myLastCreatedElems.Append(newElem1);
2969       myLastCreatedElems.Append(newElem2);
2970       // put a new triangle on the same shape and add to the same groups
2971       if ( aShapeId )
2972         {
2973           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975         }
2976       AddToSameGroups( newElem1, elem, aMesh );
2977       AddToSameGroups( newElem2, elem, aMesh );
2978       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2979       aMesh->RemoveElement( elem );
2980     }
2981
2982     // Quadratic quadrangle
2983
2984     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2985
2986       // get surface elem is on
2987       int aShapeId = FindShape( elem );
2988       if ( aShapeId != helper.GetSubShapeID() ) {
2989         surface.Nullify();
2990         TopoDS_Shape shape;
2991         if ( aShapeId > 0 )
2992           shape = aMesh->IndexToShape( aShapeId );
2993         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2994           TopoDS_Face face = TopoDS::Face( shape );
2995           surface = BRep_Tool::Surface( face );
2996           if ( !surface.IsNull() )
2997             helper.SetSubShape( shape );
2998         }
2999       }
3000
3001       const SMDS_MeshNode* aNodes [8];
3002       const SMDS_MeshNode* inFaceNode = 0;
3003       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004       int i = 0;
3005       while ( itN->more() ) {
3006         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3007         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3008              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3009         {
3010           inFaceNode = aNodes[ i-1 ];
3011         }
3012       }
3013
3014       // find middle point for (0,1,2,3)
3015       // and create a node in this point;
3016       gp_XYZ p( 0,0,0 );
3017       if ( surface.IsNull() ) {
3018         for(i=0; i<4; i++)
3019           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3020         p /= 4;
3021       }
3022       else {
3023         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3024         gp_XY uv( 0,0 );
3025         for(i=0; i<4; i++)
3026           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3027         uv /= 4.;
3028         p = surface->Value( uv.X(), uv.Y() ).XYZ();
3029       }
3030       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3031       myLastCreatedNodes.Append(newN);
3032
3033       // create a new element
3034       const SMDS_MeshElement* newElem1 = 0;
3035       const SMDS_MeshElement* newElem2 = 0;
3036       if ( the13Diag ) {
3037         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3038                                   aNodes[6], aNodes[7], newN );
3039         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040                                   newN,      aNodes[4], aNodes[5] );
3041       }
3042       else {
3043         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044                                   aNodes[7], aNodes[4], newN );
3045         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046                                   newN,      aNodes[5], aNodes[6] );
3047       }
3048       myLastCreatedElems.Append(newElem1);
3049       myLastCreatedElems.Append(newElem2);
3050       // put a new triangle on the same shape and add to the same groups
3051       if ( aShapeId )
3052         {
3053           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3054           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3055         }
3056       AddToSameGroups( newElem1, elem, aMesh );
3057       AddToSameGroups( newElem2, elem, aMesh );
3058       aMesh->RemoveElement( elem );
3059     }
3060   }
3061
3062   return true;
3063 }
3064
3065 //=======================================================================
3066 //function : getAngle
3067 //purpose  :
3068 //=======================================================================
3069
3070 double getAngle(const SMDS_MeshElement * tr1,
3071                 const SMDS_MeshElement * tr2,
3072                 const SMDS_MeshNode *    n1,
3073                 const SMDS_MeshNode *    n2)
3074 {
3075   double angle = 2. * M_PI; // bad angle
3076
3077   // get normals
3078   SMESH::Controls::TSequenceOfXYZ P1, P2;
3079   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3080        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3081     return angle;
3082   gp_Vec N1,N2;
3083   if(!tr1->IsQuadratic())
3084     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3085   else
3086     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3087   if ( N1.SquareMagnitude() <= gp::Resolution() )
3088     return angle;
3089   if(!tr2->IsQuadratic())
3090     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3091   else
3092     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3093   if ( N2.SquareMagnitude() <= gp::Resolution() )
3094     return angle;
3095
3096   // find the first diagonal node n1 in the triangles:
3097   // take in account a diagonal link orientation
3098   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3099   for ( int t = 0; t < 2; t++ ) {
3100     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3101     int i = 0, iDiag = -1;
3102     while ( it->more()) {
3103       const SMDS_MeshElement *n = it->next();
3104       if ( n == n1 || n == n2 ) {
3105         if ( iDiag < 0)
3106           iDiag = i;
3107         else {
3108           if ( i - iDiag == 1 )
3109             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3110           else
3111             nFirst[ t ] = n;
3112           break;
3113         }
3114       }
3115       i++;
3116     }
3117   }
3118   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3119     N2.Reverse();
3120
3121   angle = N1.Angle( N2 );
3122   //SCRUTE( angle );
3123   return angle;
3124 }
3125
3126 // =================================================
3127 // class generating a unique ID for a pair of nodes
3128 // and able to return nodes by that ID
3129 // =================================================
3130 class LinkID_Gen {
3131 public:
3132
3133   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3134     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3135   {}
3136
3137   long GetLinkID (const SMDS_MeshNode * n1,
3138                   const SMDS_MeshNode * n2) const
3139   {
3140     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3141   }
3142
3143   bool GetNodes (const long             theLinkID,
3144                  const SMDS_MeshNode* & theNode1,
3145                  const SMDS_MeshNode* & theNode2) const
3146   {
3147     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3148     if ( !theNode1 ) return false;
3149     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3150     if ( !theNode2 ) return false;
3151     return true;
3152   }
3153
3154 private:
3155   LinkID_Gen();
3156   const SMESHDS_Mesh* myMesh;
3157   long                myMaxID;
3158 };
3159
3160
3161 //=======================================================================
3162 //function : TriToQuad
3163 //purpose  : Fuse neighbour triangles into quadrangles.
3164 //           theCrit is used to select a neighbour to fuse with.
3165 //           theMaxAngle is a max angle between element normals at which
3166 //           fusion is still performed.
3167 //=======================================================================
3168
3169 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3170                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3171                                   const double                         theMaxAngle)
3172 {
3173   myLastCreatedElems.Clear();
3174   myLastCreatedNodes.Clear();
3175
3176   MESSAGE( "::TriToQuad()" );
3177
3178   if ( !theCrit.get() )
3179     return false;
3180
3181   SMESHDS_Mesh * aMesh = GetMeshDS();
3182
3183   // Prepare data for algo: build
3184   // 1. map of elements with their linkIDs
3185   // 2. map of linkIDs with their elements
3186
3187   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3188   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3189   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3190   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3191
3192   TIDSortedElemSet::iterator itElem;
3193   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3194   {
3195     const SMDS_MeshElement* elem = *itElem;
3196     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3197     bool IsTria = ( elem->NbCornerNodes()==3 );
3198     if (!IsTria) continue;
3199
3200     // retrieve element nodes
3201     const SMDS_MeshNode* aNodes [4];
3202     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3203     int i = 0;
3204     while ( i < 3 )
3205       aNodes[ i++ ] = itN->next();
3206     aNodes[ 3 ] = aNodes[ 0 ];
3207
3208     // fill maps
3209     for ( i = 0; i < 3; i++ ) {
3210       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3211       // check if elements sharing a link can be fused
3212       itLE = mapLi_listEl.find( link );
3213       if ( itLE != mapLi_listEl.end() ) {
3214         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3215           continue;
3216         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3217         //if ( FindShape( elem ) != FindShape( elem2 ))
3218         //  continue; // do not fuse triangles laying on different shapes
3219         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3220           continue; // avoid making badly shaped quads
3221         (*itLE).second.push_back( elem );
3222       }
3223       else {
3224         mapLi_listEl[ link ].push_back( elem );
3225       }
3226       mapEl_setLi [ elem ].insert( link );
3227     }
3228   }
3229   // Clean the maps from the links shared by a sole element, ie
3230   // links to which only one element is bound in mapLi_listEl
3231
3232   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3233     int nbElems = (*itLE).second.size();
3234     if ( nbElems < 2  ) {
3235       const SMDS_MeshElement* elem = (*itLE).second.front();
3236       SMESH_TLink link = (*itLE).first;
3237       mapEl_setLi[ elem ].erase( link );
3238       if ( mapEl_setLi[ elem ].empty() )
3239         mapEl_setLi.erase( elem );
3240     }
3241   }
3242
3243   // Algo: fuse triangles into quadrangles
3244
3245   while ( ! mapEl_setLi.empty() ) {
3246     // Look for the start element:
3247     // the element having the least nb of shared links
3248     const SMDS_MeshElement* startElem = 0;
3249     int minNbLinks = 4;
3250     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3251       int nbLinks = (*itEL).second.size();
3252       if ( nbLinks < minNbLinks ) {
3253         startElem = (*itEL).first;
3254         minNbLinks = nbLinks;
3255         if ( minNbLinks == 1 )
3256           break;
3257       }
3258     }
3259
3260     // search elements to fuse starting from startElem or links of elements
3261     // fused earlyer - startLinks
3262     list< SMESH_TLink > startLinks;
3263     while ( startElem || !startLinks.empty() ) {
3264       while ( !startElem && !startLinks.empty() ) {
3265         // Get an element to start, by a link
3266         SMESH_TLink linkId = startLinks.front();
3267         startLinks.pop_front();
3268         itLE = mapLi_listEl.find( linkId );
3269         if ( itLE != mapLi_listEl.end() ) {
3270           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3271           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3272           for ( ; itE != listElem.end() ; itE++ )
3273             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3274               startElem = (*itE);
3275           mapLi_listEl.erase( itLE );
3276         }
3277       }
3278
3279       if ( startElem ) {
3280         // Get candidates to be fused
3281         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3282         const SMESH_TLink *link12, *link13;
3283         startElem = 0;
3284         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3285         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3286         ASSERT( !setLi.empty() );
3287         set< SMESH_TLink >::iterator itLi;
3288         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3289         {
3290           const SMESH_TLink & link = (*itLi);
3291           itLE = mapLi_listEl.find( link );
3292           if ( itLE == mapLi_listEl.end() )
3293             continue;
3294
3295           const SMDS_MeshElement* elem = (*itLE).second.front();
3296           if ( elem == tr1 )
3297             elem = (*itLE).second.back();
3298           mapLi_listEl.erase( itLE );
3299           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3300             continue;
3301           if ( tr2 ) {
3302             tr3 = elem;
3303             link13 = &link;
3304           }
3305           else {
3306             tr2 = elem;
3307             link12 = &link;
3308           }
3309
3310           // add other links of elem to list of links to re-start from
3311           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3312           set< SMESH_TLink >::iterator it;
3313           for ( it = links.begin(); it != links.end(); it++ ) {
3314             const SMESH_TLink& link2 = (*it);
3315             if ( link2 != link )
3316               startLinks.push_back( link2 );
3317           }
3318         }
3319
3320         // Get nodes of possible quadrangles
3321         const SMDS_MeshNode *n12 [4], *n13 [4];
3322         bool Ok12 = false, Ok13 = false;
3323         const SMDS_MeshNode *linkNode1, *linkNode2;
3324         if(tr2) {
3325           linkNode1 = link12->first;
3326           linkNode2 = link12->second;
3327           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3328             Ok12 = true;
3329         }
3330         if(tr3) {
3331           linkNode1 = link13->first;
3332           linkNode2 = link13->second;
3333           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3334             Ok13 = true;
3335         }
3336
3337         // Choose a pair to fuse
3338         if ( Ok12 && Ok13 ) {
3339           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3340           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3341           double aBadRate12 = getBadRate( &quad12, theCrit );
3342           double aBadRate13 = getBadRate( &quad13, theCrit );
3343           if (  aBadRate13 < aBadRate12 )
3344             Ok12 = false;
3345           else
3346             Ok13 = false;
3347         }
3348
3349         // Make quadrangles
3350         // and remove fused elems and remove links from the maps
3351         mapEl_setLi.erase( tr1 );
3352         if ( Ok12 )
3353         {
3354           mapEl_setLi.erase( tr2 );
3355           mapLi_listEl.erase( *link12 );
3356           if ( tr1->NbNodes() == 3 )
3357           {
3358             const SMDS_MeshElement* newElem = 0;
3359             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3360             myLastCreatedElems.Append(newElem);
3361             AddToSameGroups( newElem, tr1, aMesh );
3362             int aShapeId = tr1->getshapeId();
3363             if ( aShapeId )
3364               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3365             aMesh->RemoveElement( tr1 );
3366             aMesh->RemoveElement( tr2 );
3367           }
3368           else {
3369             vector< const SMDS_MeshNode* > N1;
3370             vector< const SMDS_MeshNode* > N2;
3371             getNodesFromTwoTria(tr1,tr2,N1,N2);
3372             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3373             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3374             // i.e. first nodes from both arrays form a new diagonal
3375             const SMDS_MeshNode* aNodes[8];
3376             aNodes[0] = N1[0];
3377             aNodes[1] = N1[1];
3378             aNodes[2] = N2[0];
3379             aNodes[3] = N2[1];
3380             aNodes[4] = N1[3];
3381             aNodes[5] = N2[5];
3382             aNodes[6] = N2[3];
3383             aNodes[7] = N1[5];
3384             const SMDS_MeshElement* newElem = 0;
3385             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3386               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3387                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3388             else
3389               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3390                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3391             myLastCreatedElems.Append(newElem);
3392             AddToSameGroups( newElem, tr1, aMesh );
3393             int aShapeId = tr1->getshapeId();
3394             if ( aShapeId )
3395               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3396             aMesh->RemoveElement( tr1 );
3397             aMesh->RemoveElement( tr2 );
3398             // remove middle node (9)
3399             if ( N1[4]->NbInverseElements() == 0 )
3400               aMesh->RemoveNode( N1[4] );
3401             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3402               aMesh->RemoveNode( N1[6] );
3403             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3404               aMesh->RemoveNode( N2[6] );
3405           }
3406         }
3407         else if ( Ok13 )
3408         {
3409           mapEl_setLi.erase( tr3 );
3410           mapLi_listEl.erase( *link13 );
3411           if ( tr1->NbNodes() == 3 ) {
3412             const SMDS_MeshElement* newElem = 0;
3413             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3414             myLastCreatedElems.Append(newElem);
3415             AddToSameGroups( newElem, tr1, aMesh );
3416             int aShapeId = tr1->getshapeId();
3417             if ( aShapeId )
3418               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3419             aMesh->RemoveElement( tr1 );
3420             aMesh->RemoveElement( tr3 );
3421           }
3422           else {
3423             vector< const SMDS_MeshNode* > N1;
3424             vector< const SMDS_MeshNode* > N2;
3425             getNodesFromTwoTria(tr1,tr3,N1,N2);
3426             // now we receive following N1 and N2 (using numeration as above image)
3427             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3428             // i.e. first nodes from both arrays form a new diagonal
3429             const SMDS_MeshNode* aNodes[8];
3430             aNodes[0] = N1[0];
3431             aNodes[1] = N1[1];
3432             aNodes[2] = N2[0];
3433             aNodes[3] = N2[1];
3434             aNodes[4] = N1[3];
3435             aNodes[5] = N2[5];
3436             aNodes[6] = N2[3];
3437             aNodes[7] = N1[5];
3438             const SMDS_MeshElement* newElem = 0;
3439             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3440               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3441                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3442             else
3443               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3444                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3445             myLastCreatedElems.Append(newElem);
3446             AddToSameGroups( newElem, tr1, aMesh );
3447             int aShapeId = tr1->getshapeId();
3448             if ( aShapeId )
3449               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3450             aMesh->RemoveElement( tr1 );
3451             aMesh->RemoveElement( tr3 );
3452             // remove middle node (9)
3453             if ( N1[4]->NbInverseElements() == 0 )
3454               aMesh->RemoveNode( N1[4] );
3455             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3456               aMesh->RemoveNode( N1[6] );
3457             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3458               aMesh->RemoveNode( N2[6] );
3459           }
3460         }
3461
3462         // Next element to fuse: the rejected one
3463         if ( tr3 )
3464           startElem = Ok12 ? tr3 : tr2;
3465
3466       } // if ( startElem )
3467     } // while ( startElem || !startLinks.empty() )
3468   } // while ( ! mapEl_setLi.empty() )
3469
3470   return true;
3471 }
3472
3473
3474 /*#define DUMPSO(txt) \
3475 //  cout << txt << endl;
3476 //=============================================================================
3477 //
3478 //
3479 //
3480 //=============================================================================
3481 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3482 {
3483 if ( i1 == i2 )
3484 return;
3485 int tmp = idNodes[ i1 ];
3486 idNodes[ i1 ] = idNodes[ i2 ];
3487 idNodes[ i2 ] = tmp;
3488 gp_Pnt Ptmp = P[ i1 ];
3489 P[ i1 ] = P[ i2 ];
3490 P[ i2 ] = Ptmp;
3491 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3492 }
3493
3494 //=======================================================================
3495 //function : SortQuadNodes
3496 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3497 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3498 //           1 or 2 else 0.
3499 //=======================================================================
3500
3501 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3502 int               idNodes[] )
3503 {
3504   gp_Pnt P[4];
3505   int i;
3506   for ( i = 0; i < 4; i++ ) {
3507     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3508     if ( !n ) return 0;
3509     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3510   }
3511
3512   gp_Vec V1(P[0], P[1]);
3513   gp_Vec V2(P[0], P[2]);
3514   gp_Vec V3(P[0], P[3]);
3515
3516   gp_Vec Cross1 = V1 ^ V2;
3517   gp_Vec Cross2 = V2 ^ V3;
3518
3519   i = 0;
3520   if (Cross1.Dot(Cross2) < 0)
3521   {
3522     Cross1 = V2 ^ V1;
3523     Cross2 = V1 ^ V3;
3524
3525     if (Cross1.Dot(Cross2) < 0)
3526       i = 2;
3527     else
3528       i = 1;
3529     swap ( i, i + 1, idNodes, P );
3530
3531     //     for ( int ii = 0; ii < 4; ii++ ) {
3532     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3533     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534     //     }
3535   }
3536   return i;
3537 }
3538
3539 //=======================================================================
3540 //function : SortHexaNodes
3541 //purpose  : Set 8 nodes of a hexahedron in a good order.
3542 //           Return success status
3543 //=======================================================================
3544
3545 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3546                                       int               idNodes[] )
3547 {
3548   gp_Pnt P[8];
3549   int i;
3550   DUMPSO( "INPUT: ========================================");
3551   for ( i = 0; i < 8; i++ ) {
3552     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3553     if ( !n ) return false;
3554     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3555     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3556   }
3557   DUMPSO( "========================================");
3558
3559
3560   set<int> faceNodes;  // ids of bottom face nodes, to be found
3561   set<int> checkedId1; // ids of tried 2-nd nodes
3562   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3563   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3564   int iMin, iLoop1 = 0;
3565
3566   // Loop to try the 2-nd nodes
3567
3568   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3569   {
3570     // Find not checked 2-nd node
3571     for ( i = 1; i < 8; i++ )
3572       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3573         int id1 = idNodes[i];
3574         swap ( 1, i, idNodes, P );
3575         checkedId1.insert ( id1 );
3576         break;
3577       }
3578
3579     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3580     // ie that all but meybe one (id3 which is on the same face) nodes
3581     // lay on the same side from the triangle plane.
3582
3583     bool manyInPlane = false; // more than 4 nodes lay in plane
3584     int iLoop2 = 0;
3585     while ( ++iLoop2 < 6 ) {
3586
3587       // get 1-2-3 plane coeffs
3588       Standard_Real A, B, C, D;
3589       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3590       if ( N.SquareMagnitude() > gp::Resolution() )
3591       {
3592         gp_Pln pln ( P[0], N );
3593         pln.Coefficients( A, B, C, D );
3594
3595         // find the node (iMin) closest to pln
3596         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3597         set<int> idInPln;
3598         for ( i = 3; i < 8; i++ ) {
3599           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3600           if ( fabs( dist[i] ) < minDist ) {
3601             minDist = fabs( dist[i] );
3602             iMin = i;
3603           }
3604           if ( fabs( dist[i] ) <= tol )
3605             idInPln.insert( idNodes[i] );
3606         }
3607
3608         // there should not be more than 4 nodes in bottom plane
3609         if ( idInPln.size() > 1 )
3610         {
3611           DUMPSO( "### idInPln.size() = " << idInPln.size());
3612           // idInPlane does not contain the first 3 nodes
3613           if ( manyInPlane || idInPln.size() == 5)
3614             return false; // all nodes in one plane
3615           manyInPlane = true;
3616
3617           // set the 1-st node to be not in plane
3618           for ( i = 3; i < 8; i++ ) {
3619             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3620               DUMPSO( "### Reset 0-th node");
3621               swap( 0, i, idNodes, P );
3622               break;
3623             }
3624           }
3625
3626           // reset to re-check second nodes
3627           leastDist = DBL_MAX;
3628           faceNodes.clear();
3629           checkedId1.clear();
3630           iLoop1 = 0;
3631           break; // from iLoop2;
3632         }
3633
3634         // check that the other 4 nodes are on the same side
3635         bool sameSide = true;
3636         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3637         for ( i = 3; sameSide && i < 8; i++ ) {
3638           if ( i != iMin )
3639             sameSide = ( isNeg == dist[i] <= 0.);
3640         }
3641
3642         // keep best solution
3643         if ( sameSide && minDist < leastDist ) {
3644           leastDist = minDist;
3645           faceNodes.clear();
3646           faceNodes.insert( idNodes[ 1 ] );
3647           faceNodes.insert( idNodes[ 2 ] );
3648           faceNodes.insert( idNodes[ iMin ] );
3649           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3650                   << " leastDist = " << leastDist);
3651           if ( leastDist <= DBL_MIN )
3652             break;
3653         }
3654       }
3655
3656       // set next 3-d node to check
3657       int iNext = 2 + iLoop2;
3658       if ( iNext < 8 ) {
3659         DUMPSO( "Try 2-nd");
3660         swap ( 2, iNext, idNodes, P );
3661       }
3662     } // while ( iLoop2 < 6 )
3663   } // iLoop1
3664
3665   if ( faceNodes.empty() ) return false;
3666
3667   // Put the faceNodes in proper places
3668   for ( i = 4; i < 8; i++ ) {
3669     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3670       // find a place to put
3671       int iTo = 1;
3672       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3673         iTo++;
3674       DUMPSO( "Set faceNodes");
3675       swap ( iTo, i, idNodes, P );
3676     }
3677   }
3678
3679
3680   // Set nodes of the found bottom face in good order
3681   DUMPSO( " Found bottom face: ");
3682   i = SortQuadNodes( theMesh, idNodes );
3683   if ( i ) {
3684     gp_Pnt Ptmp = P[ i ];
3685     P[ i ] = P[ i+1 ];
3686     P[ i+1 ] = Ptmp;
3687   }
3688   //   else
3689   //     for ( int ii = 0; ii < 4; ii++ ) {
3690   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3691   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3692   //    }
3693
3694   // Gravity center of the top and bottom faces
3695   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3696   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3697
3698   // Get direction from the bottom to the top face
3699   gp_Vec upDir ( aGCb, aGCt );
3700   Standard_Real upDirSize = upDir.Magnitude();
3701   if ( upDirSize <= gp::Resolution() ) return false;
3702   upDir / upDirSize;
3703
3704   // Assure that the bottom face normal points up
3705   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3706   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3707   if ( Nb.Dot( upDir ) < 0 ) {
3708     DUMPSO( "Reverse bottom face");
3709     swap( 1, 3, idNodes, P );
3710   }
3711
3712   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3713   Standard_Real minDist = DBL_MAX;
3714   for ( i = 4; i < 8; i++ ) {
3715     // projection of P[i] to the plane defined by P[0] and upDir
3716     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3717     Standard_Real sqDist = P[0].SquareDistance( Pp );
3718     if ( sqDist < minDist ) {
3719       minDist = sqDist;
3720       iMin = i;
3721     }
3722   }
3723   DUMPSO( "Set 4-th");
3724   swap ( 4, iMin, idNodes, P );
3725
3726   // Set nodes of the top face in good order
3727   DUMPSO( "Sort top face");
3728   i = SortQuadNodes( theMesh, &idNodes[4] );
3729   if ( i ) {
3730     i += 4;
3731     gp_Pnt Ptmp = P[ i ];
3732     P[ i ] = P[ i+1 ];
3733     P[ i+1 ] = Ptmp;
3734   }
3735
3736   // Assure that direction of the top face normal is from the bottom face
3737   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3738   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3739   if ( Nt.Dot( upDir ) < 0 ) {
3740     DUMPSO( "Reverse top face");
3741     swap( 5, 7, idNodes, P );
3742   }
3743
3744   //   DUMPSO( "OUTPUT: ========================================");
3745   //   for ( i = 0; i < 8; i++ ) {
3746   //     float *p = ugrid->GetPoint(idNodes[i]);
3747   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3748   //   }
3749
3750   return true;
3751 }*/
3752
3753 //================================================================================
3754 /*!
3755  * \brief Return nodes linked to the given one
3756  * \param theNode - the node
3757  * \param linkedNodes - the found nodes
3758  * \param type - the type of elements to check
3759  *
3760  * Medium nodes are ignored
3761  */
3762 //================================================================================
3763
3764 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3765                                        TIDSortedElemSet &   linkedNodes,
3766                                        SMDSAbs_ElementType  type )
3767 {
3768   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3769   while ( elemIt->more() )
3770   {
3771     const SMDS_MeshElement* elem = elemIt->next();
3772     if(elem->GetType() == SMDSAbs_0DElement)
3773       continue;
3774
3775     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3776     if ( elem->GetType() == SMDSAbs_Volume )
3777     {
3778       SMDS_VolumeTool vol( elem );
3779       while ( nodeIt->more() ) {
3780         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3781         if ( theNode != n && vol.IsLinked( theNode, n ))
3782           linkedNodes.insert( n );
3783       }
3784     }
3785     else
3786     {
3787       for ( int i = 0; nodeIt->more(); ++i ) {
3788         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3789         if ( n == theNode ) {
3790           int iBefore = i - 1;
3791           int iAfter  = i + 1;
3792           if ( elem->IsQuadratic() ) {
3793             int nb = elem->NbNodes() / 2;
3794             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3795             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3796           }
3797           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3798           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3799         }
3800       }
3801     }
3802   }
3803 }
3804
3805 //=======================================================================
3806 //function : laplacianSmooth
3807 //purpose  : pulls theNode toward the center of surrounding nodes directly
3808 //           connected to that node along an element edge
3809 //=======================================================================
3810
3811 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3812                      const Handle(Geom_Surface)&          theSurface,
3813                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3814 {
3815   // find surrounding nodes
3816
3817   TIDSortedElemSet nodeSet;
3818   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3819
3820   // compute new coodrs
3821
3822   double coord[] = { 0., 0., 0. };
3823   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3824   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3825     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3826     if ( theSurface.IsNull() ) { // smooth in 3D
3827       coord[0] += node->X();
3828       coord[1] += node->Y();
3829       coord[2] += node->Z();
3830     }
3831     else { // smooth in 2D
3832       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3833       gp_XY* uv = theUVMap[ node ];
3834       coord[0] += uv->X();
3835       coord[1] += uv->Y();
3836     }
3837   }
3838   int nbNodes = nodeSet.size();
3839   if ( !nbNodes )
3840     return;
3841   coord[0] /= nbNodes;
3842   coord[1] /= nbNodes;
3843
3844   if ( !theSurface.IsNull() ) {
3845     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3846     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3847     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3848     coord[0] = p3d.X();
3849     coord[1] = p3d.Y();
3850     coord[2] = p3d.Z();
3851   }
3852   else
3853     coord[2] /= nbNodes;
3854
3855   // move node
3856
3857   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3858 }
3859
3860 //=======================================================================
3861 //function : centroidalSmooth
3862 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3863 //           surrounding elements
3864 //=======================================================================
3865
3866 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3867                       const Handle(Geom_Surface)&          theSurface,
3868                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3869 {
3870   gp_XYZ aNewXYZ(0.,0.,0.);
3871   SMESH::Controls::Area anAreaFunc;
3872   double totalArea = 0.;
3873   int nbElems = 0;
3874
3875   // compute new XYZ
3876
3877   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3878   while ( elemIt->more() )
3879   {
3880     const SMDS_MeshElement* elem = elemIt->next();
3881     nbElems++;
3882
3883     gp_XYZ elemCenter(0.,0.,0.);
3884     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3885     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3886     int nn = elem->NbNodes();
3887     if(elem->IsQuadratic()) nn = nn/2;
3888     int i=0;
3889     //while ( itN->more() ) {
3890     while ( i<nn ) {
3891       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3892       i++;
3893       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3894       aNodePoints.push_back( aP );
3895       if ( !theSurface.IsNull() ) { // smooth in 2D
3896         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3897         gp_XY* uv = theUVMap[ aNode ];
3898         aP.SetCoord( uv->X(), uv->Y(), 0. );
3899       }
3900       elemCenter += aP;
3901     }
3902     double elemArea = anAreaFunc.GetValue( aNodePoints );
3903     totalArea += elemArea;
3904     elemCenter /= nn;
3905     aNewXYZ += elemCenter * elemArea;
3906   }
3907   aNewXYZ /= totalArea;
3908   if ( !theSurface.IsNull() ) {
3909     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3910     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3911   }
3912
3913   // move node
3914
3915   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3916 }
3917
3918 //=======================================================================
3919 //function : getClosestUV
3920 //purpose  : return UV of closest projection
3921 //=======================================================================
3922
3923 static bool getClosestUV (Extrema_GenExtPS& projector,
3924                           const gp_Pnt&     point,
3925                           gp_XY &           result)
3926 {
3927   projector.Perform( point );
3928   if ( projector.IsDone() ) {
3929     double u, v, minVal = DBL_MAX;
3930     for ( int i = projector.NbExt(); i > 0; i-- )
3931       if ( projector.SquareDistance( i ) < minVal ) {
3932         minVal = projector.SquareDistance( i );
3933         projector.Point( i ).Parameter( u, v );
3934       }
3935     result.SetCoord( u, v );
3936     return true;
3937   }
3938   return false;
3939 }
3940
3941 //=======================================================================
3942 //function : Smooth
3943 //purpose  : Smooth theElements during theNbIterations or until a worst
3944 //           element has aspect ratio <= theTgtAspectRatio.
3945 //           Aspect Ratio varies in range [1.0, inf].
3946 //           If theElements is empty, the whole mesh is smoothed.
3947 //           theFixedNodes contains additionally fixed nodes. Nodes built
3948 //           on edges and boundary nodes are always fixed.
3949 //=======================================================================
3950
3951 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3952                                set<const SMDS_MeshNode*> & theFixedNodes,
3953                                const SmoothMethod          theSmoothMethod,
3954                                const int                   theNbIterations,
3955                                double                      theTgtAspectRatio,
3956                                const bool                  the2D)
3957 {
3958   myLastCreatedElems.Clear();
3959   myLastCreatedNodes.Clear();
3960
3961   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3962
3963   if ( theTgtAspectRatio < 1.0 )
3964     theTgtAspectRatio = 1.0;
3965
3966   const double disttol = 1.e-16;
3967
3968   SMESH::Controls::AspectRatio aQualityFunc;
3969
3970   SMESHDS_Mesh* aMesh = GetMeshDS();
3971
3972   if ( theElems.empty() ) {
3973     // add all faces to theElems
3974     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3975     while ( fIt->more() ) {
3976       const SMDS_MeshElement* face = fIt->next();
3977       theElems.insert( theElems.end(), face );
3978     }
3979   }
3980   // get all face ids theElems are on
3981   set< int > faceIdSet;
3982   TIDSortedElemSet::iterator itElem;
3983   if ( the2D )
3984     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3985       int fId = FindShape( *itElem );
3986       // check that corresponding submesh exists and a shape is face
3987       if (fId &&
3988           faceIdSet.find( fId ) == faceIdSet.end() &&
3989           aMesh->MeshElements( fId )) {
3990         TopoDS_Shape F = aMesh->IndexToShape( fId );
3991         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3992           faceIdSet.insert( fId );
3993       }
3994     }
3995   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3996
3997   // ===============================================
3998   // smooth elements on each TopoDS_Face separately
3999   // ===============================================
4000
4001   SMESH_MesherHelper helper( *GetMesh() );
4002
4003   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4004   for ( ; fId != faceIdSet.rend(); ++fId )
4005   {
4006     // get face surface and submesh
4007     Handle(Geom_Surface) surface;
4008     SMESHDS_SubMesh* faceSubMesh = 0;
4009     TopoDS_Face face;
4010     double fToler2 = 0, f,l;
4011     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4012     bool isUPeriodic = false, isVPeriodic = false;
4013     if ( *fId )
4014     {
4015       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4016       surface = BRep_Tool::Surface( face );
4017       faceSubMesh = aMesh->MeshElements( *fId );
4018       fToler2 = BRep_Tool::Tolerance( face );
4019       fToler2 *= fToler2 * 10.;
4020       isUPeriodic = surface->IsUPeriodic();
4021       if ( isUPeriodic )
4022         surface->UPeriod();
4023       isVPeriodic = surface->IsVPeriodic();
4024       if ( isVPeriodic )
4025         surface->VPeriod();
4026       surface->Bounds( u1, u2, v1, v2 );
4027       helper.SetSubShape( face );
4028     }
4029     // ---------------------------------------------------------
4030     // for elements on a face, find movable and fixed nodes and
4031     // compute UV for them
4032     // ---------------------------------------------------------
4033     bool checkBoundaryNodes = false;
4034     bool isQuadratic = false;
4035     set<const SMDS_MeshNode*> setMovableNodes;
4036     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4037     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4038     list< const SMDS_MeshElement* > elemsOnFace;
4039
4040     Extrema_GenExtPS projector;
4041     GeomAdaptor_Surface surfAdaptor;
4042     if ( !surface.IsNull() ) {
4043       surfAdaptor.Load( surface );
4044       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4045     }
4046     int nbElemOnFace = 0;
4047     itElem = theElems.begin();
4048     // loop on not yet smoothed elements: look for elems on a face
4049     while ( itElem != theElems.end() )
4050     {
4051       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4052         break; // all elements found
4053
4054       const SMDS_MeshElement* elem = *itElem;
4055       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4056            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4057         ++itElem;
4058         continue;
4059       }
4060       elemsOnFace.push_back( elem );
4061       theElems.erase( itElem++ );
4062       nbElemOnFace++;
4063
4064       if ( !isQuadratic )
4065         isQuadratic = elem->IsQuadratic();
4066
4067       // get movable nodes of elem
4068       const SMDS_MeshNode* node;
4069       SMDS_TypeOfPosition posType;
4070       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4071       int nn = 0, nbn =  elem->NbNodes();
4072       if(elem->IsQuadratic())
4073         nbn = nbn/2;
4074       while ( nn++ < nbn ) {
4075         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4076         const SMDS_PositionPtr& pos = node->GetPosition();
4077         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4078         if (posType != SMDS_TOP_EDGE &&
4079             posType != SMDS_TOP_VERTEX &&
4080             theFixedNodes.find( node ) == theFixedNodes.end())
4081         {
4082           // check if all faces around the node are on faceSubMesh
4083           // because a node on edge may be bound to face
4084           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4085           bool all = true;
4086           if ( faceSubMesh ) {
4087             while ( eIt->more() && all ) {
4088               const SMDS_MeshElement* e = eIt->next();
4089               all = faceSubMesh->Contains( e );
4090             }
4091           }
4092           if ( all )
4093             setMovableNodes.insert( node );
4094           else
4095             checkBoundaryNodes = true;
4096         }
4097         if ( posType == SMDS_TOP_3DSPACE )
4098           checkBoundaryNodes = true;
4099       }
4100
4101       if ( surface.IsNull() )
4102         continue;
4103
4104       // get nodes to check UV
4105       list< const SMDS_MeshNode* > uvCheckNodes;
4106       const SMDS_MeshNode* nodeInFace = 0;
4107       itN = elem->nodesIterator();
4108       nn = 0; nbn =  elem->NbNodes();
4109       if(elem->IsQuadratic())
4110         nbn = nbn/2;
4111       while ( nn++ < nbn ) {
4112         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4113         if ( node->GetPosition()->GetDim() == 2 )
4114           nodeInFace = node;
4115         if ( uvMap.find( node ) == uvMap.end() )
4116           uvCheckNodes.push_back( node );
4117         // add nodes of elems sharing node
4118         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4119         //         while ( eIt->more() ) {
4120         //           const SMDS_MeshElement* e = eIt->next();
4121         //           if ( e != elem ) {
4122         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4123         //             while ( nIt->more() ) {
4124         //               const SMDS_MeshNode* n =
4125         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4126         //               if ( uvMap.find( n ) == uvMap.end() )
4127         //                 uvCheckNodes.push_back( n );
4128         //             }
4129         //           }
4130         //         }
4131       }
4132       // check UV on face
4133       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4134       for ( ; n != uvCheckNodes.end(); ++n ) {
4135         node = *n;
4136         gp_XY uv( 0, 0 );
4137         const SMDS_PositionPtr& pos = node->GetPosition();
4138         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4139         // get existing UV
4140         if ( pos )
4141         {
4142           bool toCheck = true;
4143           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4144         }
4145         // compute not existing UV
4146         bool project = ( posType == SMDS_TOP_3DSPACE );
4147         // double dist1 = DBL_MAX, dist2 = 0;
4148         // if ( posType != SMDS_TOP_3DSPACE ) {
4149         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4150         //   project = dist1 > fToler2;
4151         // }
4152         if ( project ) { // compute new UV
4153           gp_XY newUV;
4154           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4155           if ( !getClosestUV( projector, pNode, newUV )) {
4156             MESSAGE("Node Projection Failed " << node);
4157           }
4158           else {
4159             if ( isUPeriodic )
4160               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4161             if ( isVPeriodic )
4162               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4163             // check new UV
4164             // if ( posType != SMDS_TOP_3DSPACE )
4165             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4166             // if ( dist2 < dist1 )
4167               uv = newUV;
4168           }
4169         }
4170         // store UV in the map
4171         listUV.push_back( uv );
4172         uvMap.insert( make_pair( node, &listUV.back() ));
4173       }
4174     } // loop on not yet smoothed elements
4175
4176     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4177       checkBoundaryNodes = true;
4178
4179     // fix nodes on mesh boundary
4180
4181     if ( checkBoundaryNodes ) {
4182       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4183       map< SMESH_TLink, int >::iterator link_nb;
4184       // put all elements links to linkNbMap
4185       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4187         const SMDS_MeshElement* elem = (*elemIt);
4188         int nbn =  elem->NbCornerNodes();
4189         // loop on elem links: insert them in linkNbMap
4190         for ( int iN = 0; iN < nbn; ++iN ) {
4191           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4192           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4193           SMESH_TLink link( n1, n2 );
4194           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4195           link_nb->second++;
4196         }
4197       }
4198       // remove nodes that are in links encountered only once from setMovableNodes
4199       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4200         if ( link_nb->second == 1 ) {
4201           setMovableNodes.erase( link_nb->first.node1() );
4202           setMovableNodes.erase( link_nb->first.node2() );
4203         }
4204       }
4205     }
4206
4207     // -----------------------------------------------------
4208     // for nodes on seam edge, compute one more UV ( uvMap2 );
4209     // find movable nodes linked to nodes on seam and which
4210     // are to be smoothed using the second UV ( uvMap2 )
4211     // -----------------------------------------------------
4212
4213     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4214     if ( !surface.IsNull() ) {
4215       TopExp_Explorer eExp( face, TopAbs_EDGE );
4216       for ( ; eExp.More(); eExp.Next() ) {
4217         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4218         if ( !BRep_Tool::IsClosed( edge, face ))
4219           continue;
4220         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4221         if ( !sm ) continue;
4222         // find out which parameter varies for a node on seam
4223         double f,l;
4224         gp_Pnt2d uv1, uv2;
4225         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4226         if ( pcurve.IsNull() ) continue;
4227         uv1 = pcurve->Value( f );
4228         edge.Reverse();
4229         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4230         if ( pcurve.IsNull() ) continue;
4231         uv2 = pcurve->Value( f );
4232         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4233         // assure uv1 < uv2
4234         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4235           std::swap( uv1, uv2 );
4236         // get nodes on seam and its vertices
4237         list< const SMDS_MeshNode* > seamNodes;
4238         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4239         while ( nSeamIt->more() ) {
4240           const SMDS_MeshNode* node = nSeamIt->next();
4241           if ( !isQuadratic || !IsMedium( node ))
4242             seamNodes.push_back( node );
4243         }
4244         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4245         for ( ; vExp.More(); vExp.Next() ) {
4246           sm = aMesh->MeshElements( vExp.Current() );
4247           if ( sm ) {
4248             nSeamIt = sm->GetNodes();
4249             while ( nSeamIt->more() )
4250               seamNodes.push_back( nSeamIt->next() );
4251           }
4252         }
4253         // loop on nodes on seam
4254         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4255         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4256           const SMDS_MeshNode* nSeam = *noSeIt;
4257           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4258           if ( n_uv == uvMap.end() )
4259             continue;
4260           // set the first UV
4261           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4262           // set the second UV
4263           listUV.push_back( *n_uv->second );
4264           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4265           if ( uvMap2.empty() )
4266             uvMap2 = uvMap; // copy the uvMap contents
4267           uvMap2[ nSeam ] = &listUV.back();
4268
4269           // collect movable nodes linked to ones on seam in nodesNearSeam
4270           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4271           while ( eIt->more() ) {
4272             const SMDS_MeshElement* e = eIt->next();
4273             int nbUseMap1 = 0, nbUseMap2 = 0;
4274             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4275             int nn = 0, nbn =  e->NbNodes();
4276             if(e->IsQuadratic()) nbn = nbn/2;
4277             while ( nn++ < nbn )
4278             {
4279               const SMDS_MeshNode* n =
4280                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4281               if (n == nSeam ||
4282                   setMovableNodes.find( n ) == setMovableNodes.end() )
4283                 continue;
4284               // add only nodes being closer to uv2 than to uv1
4285               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4286               //              0.5 * ( n->Y() + nSeam->Y() ),
4287               //              0.5 * ( n->Z() + nSeam->Z() ));
4288               // gp_XY uv;
4289               // getClosestUV( projector, pMid, uv );
4290               double x = uvMap[ n ]->Coord( iPar );
4291               if ( Abs( uv1.Coord( iPar ) - x ) >
4292                    Abs( uv2.Coord( iPar ) - x )) {
4293                 nodesNearSeam.insert( n );
4294                 nbUseMap2++;
4295               }
4296               else
4297                 nbUseMap1++;
4298             }
4299             // for centroidalSmooth all element nodes must
4300             // be on one side of a seam
4301             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4302               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4303               nn = 0;
4304               while ( nn++ < nbn ) {
4305                 const SMDS_MeshNode* n =
4306                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4307                 setMovableNodes.erase( n );
4308               }
4309             }
4310           }
4311         } // loop on nodes on seam
4312       } // loop on edge of a face
4313     } // if ( !face.IsNull() )
4314
4315     if ( setMovableNodes.empty() ) {
4316       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4317       continue; // goto next face
4318     }
4319
4320     // -------------
4321     // SMOOTHING //
4322     // -------------
4323
4324     int it = -1;
4325     double maxRatio = -1., maxDisplacement = -1.;
4326     set<const SMDS_MeshNode*>::iterator nodeToMove;
4327     for ( it = 0; it < theNbIterations; it++ ) {
4328       maxDisplacement = 0.;
4329       nodeToMove = setMovableNodes.begin();
4330       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4331         const SMDS_MeshNode* node = (*nodeToMove);
4332         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4333
4334         // smooth
4335         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4336         if ( theSmoothMethod == LAPLACIAN )
4337           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4338         else
4339           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4340
4341         // node displacement
4342         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4343         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4344         if ( aDispl > maxDisplacement )
4345           maxDisplacement = aDispl;
4346       }
4347       // no node movement => exit
4348       //if ( maxDisplacement < 1.e-16 ) {
4349       if ( maxDisplacement < disttol ) {
4350         MESSAGE("-- no node movement --");
4351         break;
4352       }
4353
4354       // check elements quality
4355       maxRatio  = 0;
4356       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4357       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4358         const SMDS_MeshElement* elem = (*elemIt);
4359         if ( !elem || elem->GetType() != SMDSAbs_Face )
4360           continue;
4361         SMESH::Controls::TSequenceOfXYZ aPoints;
4362         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4363           double aValue = aQualityFunc.GetValue( aPoints );
4364           if ( aValue > maxRatio )
4365             maxRatio = aValue;
4366         }
4367       }
4368       if ( maxRatio <= theTgtAspectRatio ) {
4369         MESSAGE("-- quality achived --");
4370         break;
4371       }
4372       if (it+1 == theNbIterations) {
4373         MESSAGE("-- Iteration limit exceeded --");
4374       }
4375     } // smoothing iterations
4376
4377     MESSAGE(" Face id: " << *fId <<
4378             " Nb iterstions: " << it <<
4379             " Displacement: " << maxDisplacement <<
4380             " Aspect Ratio " << maxRatio);
4381
4382     // ---------------------------------------
4383     // new nodes positions are computed,
4384     // record movement in DS and set new UV
4385     // ---------------------------------------
4386     nodeToMove = setMovableNodes.begin();
4387     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4388       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4389       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4390       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4391       if ( node_uv != uvMap.end() ) {
4392         gp_XY* uv = node_uv->second;
4393         node->SetPosition
4394           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4395       }
4396     }
4397
4398     // move medium nodes of quadratic elements
4399     if ( isQuadratic )
4400     {
4401       vector<const SMDS_MeshNode*> nodes;
4402       bool checkUV;
4403       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4404       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4405       {
4406         const SMDS_MeshElement* QF = *elemIt;
4407         if ( QF->IsQuadratic() )
4408         {
4409           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4410                         SMDS_MeshElement::iterator() );
4411           nodes.push_back( nodes[0] );
4412           gp_Pnt xyz;
4413           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4414           {
4415             if ( !surface.IsNull() )
4416             {
4417               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4418               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4419               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4420               xyz = surface->Value( uv.X(), uv.Y() );
4421             }
4422             else {
4423               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4424             }
4425             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4426               // we have to move a medium node
4427               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4428           }
4429         }
4430       }
4431     }
4432
4433   } // loop on face ids
4434
4435 }
4436
4437 namespace
4438 {
4439   //=======================================================================
4440   //function : isReverse
4441   //purpose  : Return true if normal of prevNodes is not co-directied with
4442   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4443   //           iNotSame is where prevNodes and nextNodes are different.
4444   //           If result is true then future volume orientation is OK
4445   //=======================================================================
4446
4447   bool isReverse(const SMDS_MeshElement*             face,
4448                  const vector<const SMDS_MeshNode*>& prevNodes,
4449                  const vector<const SMDS_MeshNode*>& nextNodes,
4450                  const int                           iNotSame)
4451   {
4452
4453     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4454     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4455     gp_XYZ extrDir( pN - pP ), faceNorm;
4456     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4457
4458     return faceNorm * extrDir < 0.0;
4459   }
4460
4461   //================================================================================
4462   /*!
4463    * \brief Assure that theElemSets[0] holds elements, not nodes
4464    */
4465   //================================================================================
4466
4467   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4468   {
4469     if ( !theElemSets[0].empty() &&
4470          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4471     {
4472       std::swap( theElemSets[0], theElemSets[1] );
4473     }
4474     else if ( !theElemSets[1].empty() &&
4475               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4476     {
4477       std::swap( theElemSets[0], theElemSets[1] );
4478     }
4479   }
4480 }
4481
4482 //=======================================================================
4483 /*!
4484  * \brief Create elements by sweeping an element
4485  * \param elem - element to sweep
4486  * \param newNodesItVec - nodes generated from each node of the element
4487  * \param newElems - generated elements
4488  * \param nbSteps - number of sweeping steps
4489  * \param srcElements - to append elem for each generated element
4490  */
4491 //=======================================================================
4492
4493 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4494                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4495                                     list<const SMDS_MeshElement*>&        newElems,
4496                                     const int                             nbSteps,
4497                                     SMESH_SequenceOfElemPtr&              srcElements)
4498 {
4499   //MESSAGE("sweepElement " << nbSteps);
4500   SMESHDS_Mesh* aMesh = GetMeshDS();
4501
4502   const int           nbNodes = elem->NbNodes();
4503   const int         nbCorners = elem->NbCornerNodes();
4504   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4505                                                           polyhedron creation !!! */
4506   // Loop on elem nodes:
4507   // find new nodes and detect same nodes indices
4508   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4509   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4510   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4511   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4512
4513   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4514   vector<int> sames(nbNodes);
4515   vector<bool> isSingleNode(nbNodes);
4516
4517   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4518     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4519     const SMDS_MeshNode*                         node = nnIt->first;
4520     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4521     if ( listNewNodes.empty() )
4522       return;
4523
4524     itNN   [ iNode ] = listNewNodes.begin();
4525     prevNod[ iNode ] = node;
4526     nextNod[ iNode ] = listNewNodes.front();
4527
4528     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4529                                                              corner node of linear */
4530     if ( prevNod[ iNode ] != nextNod [ iNode ])
4531       nbDouble += !isSingleNode[iNode];
4532
4533     if( iNode < nbCorners ) { // check corners only
4534       if ( prevNod[ iNode ] == nextNod [ iNode ])
4535         sames[nbSame++] = iNode;
4536       else
4537         iNotSameNode = iNode;
4538     }
4539   }
4540
4541   if ( nbSame == nbNodes || nbSame > 2) {
4542     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4543     return;
4544   }
4545
4546   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4547   {
4548     // fix nodes order to have bottom normal external
4549     if ( baseType == SMDSEntity_Polygon )
4550     {
4551       std::reverse( itNN.begin(), itNN.end() );
4552       std::reverse( prevNod.begin(), prevNod.end() );
4553       std::reverse( midlNod.begin(), midlNod.end() );
4554       std::reverse( nextNod.begin(), nextNod.end() );
4555       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4556     }
4557     else
4558     {
4559       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4560       SMDS_MeshCell::applyInterlace( ind, itNN );
4561       SMDS_MeshCell::applyInterlace( ind, prevNod );
4562       SMDS_MeshCell::applyInterlace( ind, nextNod );
4563       SMDS_MeshCell::applyInterlace( ind, midlNod );
4564       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4565       if ( nbSame > 0 )
4566       {
4567         sames[nbSame] = iNotSameNode;
4568         for ( int j = 0; j <= nbSame; ++j )
4569           for ( size_t i = 0; i < ind.size(); ++i )
4570             if ( ind[i] == sames[j] )
4571             {
4572               sames[j] = i;
4573               break;
4574             }
4575         iNotSameNode = sames[nbSame];
4576       }
4577     }
4578   }
4579   else if ( elem->GetType() == SMDSAbs_Edge )
4580   {
4581     // orient a new face same as adjacent one
4582     int i1, i2;
4583     const SMDS_MeshElement* e;
4584     TIDSortedElemSet dummy;
4585     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4586         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4587         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4588     {
4589       // there is an adjacent face, check order of nodes in it
4590       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4591       if ( sameOrder )
4592       {
4593         std::swap( itNN[0],    itNN[1] );
4594         std::swap( prevNod[0], prevNod[1] );
4595         std::swap( nextNod[0], nextNod[1] );
4596         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4597         if ( nbSame > 0 )
4598           sames[0] = 1 - sames[0];
4599         iNotSameNode = 1 - iNotSameNode;
4600       }
4601     }
4602   }
4603
4604   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4605   if ( nbSame > 0 ) {
4606     iSameNode    = sames[ nbSame-1 ];
4607     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4608     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4609     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4610   }
4611
4612   if ( baseType == SMDSEntity_Polygon )
4613   {
4614     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4615     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4616   }
4617   else if ( baseType == SMDSEntity_Quad_Polygon )
4618   {
4619     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4620     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4621   }
4622
4623   // make new elements
4624   for (int iStep = 0; iStep < nbSteps; iStep++ )
4625   {
4626     // get next nodes
4627     for ( iNode = 0; iNode < nbNodes; iNode++ )
4628     {
4629       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4630       nextNod[ iNode ] = *itNN[ iNode ]++;
4631     }
4632
4633     SMDS_MeshElement* aNewElem = 0;
4634     /*if(!elem->IsPoly())*/ {
4635       switch ( baseType ) {
4636       case SMDSEntity_0D:
4637       case SMDSEntity_Node: { // sweep NODE
4638         if ( nbSame == 0 ) {
4639           if ( isSingleNode[0] )
4640             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4641           else
4642             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4643         }
4644         else
4645           return;
4646         break;
4647       }
4648       case SMDSEntity_Edge: { // sweep EDGE
4649         if ( nbDouble == 0 )
4650         {
4651           if ( nbSame == 0 ) // ---> quadrangle
4652             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4653                                       nextNod[ 1 ], nextNod[ 0 ] );
4654           else               // ---> triangle
4655             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4656                                       nextNod[ iNotSameNode ] );
4657         }
4658         else                 // ---> polygon
4659         {
4660           vector<const SMDS_MeshNode*> poly_nodes;
4661           poly_nodes.push_back( prevNod[0] );
4662           poly_nodes.push_back( prevNod[1] );
4663           if ( prevNod[1] != nextNod[1] )
4664           {
4665             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4666             poly_nodes.push_back( nextNod[1] );
4667           }
4668           if ( prevNod[0] != nextNod[0] )
4669           {
4670             poly_nodes.push_back( nextNod[0] );
4671             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4672           }
4673           switch ( poly_nodes.size() ) {
4674           case 3:
4675             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4676             break;
4677           case 4:
4678             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4679                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4680             break;
4681           default:
4682             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4683           }
4684         }
4685         break;
4686       }
4687       case SMDSEntity_Triangle: // TRIANGLE --->
4688         {
4689           if ( nbDouble > 0 ) break;
4690           if ( nbSame == 0 )       // ---> pentahedron
4691             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4692                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4693
4694           else if ( nbSame == 1 )  // ---> pyramid
4695             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4696                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4697                                          nextNod[ iSameNode ]);
4698
4699           else // 2 same nodes:       ---> tetrahedron
4700             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4701                                          nextNod[ iNotSameNode ]);
4702           break;
4703         }
4704       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4705         {
4706           if ( nbSame == 2 )
4707             return;
4708           if ( nbDouble+nbSame == 2 )
4709           {
4710             if(nbSame==0) {      // ---> quadratic quadrangle
4711               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4712                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4713             }
4714             else { //(nbSame==1) // ---> quadratic triangle
4715               if(sames[0]==2) {
4716                 return; // medium node on axis
4717               }
4718               else if(sames[0]==0)
4719                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4720                                           prevNod[2], midlNod[1], nextNod[2] );
4721               else // sames[0]==1
4722                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4723                                           prevNod[2], nextNod[2], midlNod[0]);
4724             }
4725           }
4726           else if ( nbDouble == 3 )
4727           {
4728             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4729               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4730                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4731             }
4732           }
4733           else
4734             return;
4735           break;
4736         }
4737       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4738         if ( nbDouble > 0 ) break;
4739
4740         if ( nbSame == 0 )       // ---> hexahedron
4741           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4742                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4743
4744         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4745           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4746                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4747                                        nextNod[ iSameNode ]);
4748           newElems.push_back( aNewElem );
4749           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4750                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4751                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4752         }
4753         else if ( nbSame == 2 ) { // ---> pentahedron
4754           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4755             // iBeforeSame is same too
4756             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4757                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4758                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4759           else
4760             // iAfterSame is same too
4761             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4762                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4763                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4764         }
4765         break;
4766       }
4767       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4768       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4769         if ( nbDouble+nbSame != 3 ) break;
4770         if(nbSame==0) {
4771           // --->  pentahedron with 15 nodes
4772           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4773                                        nextNod[0], nextNod[1], nextNod[2],
4774                                        prevNod[3], prevNod[4], prevNod[5],
4775                                        nextNod[3], nextNod[4], nextNod[5],
4776                                        midlNod[0], midlNod[1], midlNod[2]);
4777         }
4778         else if(nbSame==1) {
4779           // --->  2d order pyramid of 13 nodes
4780           int apex = iSameNode;
4781           int i0 = ( apex + 1 ) % nbCorners;
4782           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4783           int i0a = apex + 3;
4784           int i1a = i1 + 3;
4785           int i01 = i0 + 3;
4786           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4787                                       nextNod[i0], nextNod[i1], prevNod[apex],
4788                                       prevNod[i01], midlNod[i0],
4789                                       nextNod[i01], midlNod[i1],
4790                                       prevNod[i1a], prevNod[i0a],
4791                                       nextNod[i0a], nextNod[i1a]);
4792         }
4793         else if(nbSame==2) {
4794           // --->  2d order tetrahedron of 10 nodes
4795           int n1 = iNotSameNode;
4796           int n2 = ( n1 + 1             ) % nbCorners;
4797           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4798           int n12 = n1 + 3;
4799           int n23 = n2 + 3;
4800           int n31 = n3 + 3;
4801           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4802                                        prevNod[n12], prevNod[n23], prevNod[n31],
4803                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4804         }
4805         break;
4806       }
4807       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4808         if( nbSame == 0 ) {
4809           if ( nbDouble != 4 ) break;
4810           // --->  hexahedron with 20 nodes
4811           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4812                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4813                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4814                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4815                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4816         }
4817         else if(nbSame==1) {
4818           // ---> pyramid + pentahedron - can not be created since it is needed
4819           // additional middle node at the center of face
4820           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4821           return;
4822         }
4823         else if( nbSame == 2 ) {
4824           if ( nbDouble != 2 ) break;
4825           // --->  2d order Pentahedron with 15 nodes
4826           int n1,n2,n4,n5;
4827           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4828             // iBeforeSame is same too
4829             n1 = iBeforeSame;
4830             n2 = iOpposSame;
4831             n4 = iSameNode;
4832             n5 = iAfterSame;
4833           }
4834           else {
4835             // iAfterSame is same too
4836             n1 = iSameNode;
4837             n2 = iBeforeSame;
4838             n4 = iAfterSame;
4839             n5 = iOpposSame;
4840           }
4841           int n12 = n2 + 4;
4842           int n45 = n4 + 4;
4843           int n14 = n1 + 4;
4844           int n25 = n5 + 4;
4845           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4846                                        prevNod[n4], prevNod[n5], nextNod[n5],
4847                                        prevNod[n12], midlNod[n2], nextNod[n12],
4848                                        prevNod[n45], midlNod[n5], nextNod[n45],
4849                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4850         }
4851         break;
4852       }
4853       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4854
4855         if( nbSame == 0 && nbDouble == 9 ) {
4856           // --->  tri-quadratic hexahedron with 27 nodes
4857           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4858                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4859                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4860                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4861                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4862                                        prevNod[8], // bottom center
4863                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4864                                        nextNod[8], // top center
4865                                        midlNod[8]);// elem center
4866         }
4867         else
4868         {
4869           return;
4870         }
4871         break;
4872       }
4873       case SMDSEntity_Polygon: { // sweep POLYGON
4874
4875         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4876           // --->  hexagonal prism
4877           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4878                                        prevNod[3], prevNod[4], prevNod[5],
4879                                        nextNod[0], nextNod[1], nextNod[2],
4880                                        nextNod[3], nextNod[4], nextNod[5]);
4881         }
4882         break;
4883       }
4884       case SMDSEntity_Ball:
4885         return;
4886
4887       default:
4888         break;
4889       } // switch ( baseType )
4890     } // scope
4891
4892     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4893     {
4894       if ( baseType != SMDSEntity_Polygon )
4895       {
4896         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4897         SMDS_MeshCell::applyInterlace( ind, prevNod );
4898         SMDS_MeshCell::applyInterlace( ind, nextNod );
4899         SMDS_MeshCell::applyInterlace( ind, midlNod );
4900         SMDS_MeshCell::applyInterlace( ind, itNN );
4901         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4902         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4903       }
4904       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4905       vector<int> quantities (nbNodes + 2);
4906       polyedre_nodes.clear();
4907       quantities.clear();
4908
4909       // bottom of prism
4910       for (int inode = 0; inode < nbNodes; inode++)
4911         polyedre_nodes.push_back( prevNod[inode] );
4912       quantities.push_back( nbNodes );
4913
4914       // top of prism
4915       polyedre_nodes.push_back( nextNod[0] );
4916       for (int inode = nbNodes; inode-1; --inode )
4917         polyedre_nodes.push_back( nextNod[inode-1] );
4918       quantities.push_back( nbNodes );
4919
4920       // side faces
4921       // 3--6--2
4922       // |     |
4923       // 7     5
4924       // |     |
4925       // 0--4--1
4926       const int iQuad = elem->IsQuadratic();
4927       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4928       {
4929         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4930         int inextface = (iface+1+iQuad) % nbNodes;
4931         int imid      = (iface+1) % nbNodes;
4932         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4933         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4934         polyedre_nodes.push_back( prevNod[iface] );             // 1
4935         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4936         {
4937           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4938           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4939         }
4940         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4941         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4942         {
4943           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4944           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4945         }
4946         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4947         if ( nbFaceNodes > 2 )
4948           quantities.push_back( nbFaceNodes );
4949         else // degenerated face
4950           polyedre_nodes.resize( prevNbNodes );
4951       }
4952       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4953
4954     } // try to create a polyherdal prism
4955
4956     if ( aNewElem ) {
4957       newElems.push_back( aNewElem );
4958       myLastCreatedElems.Append(aNewElem);
4959       srcElements.Append( elem );
4960     }
4961
4962     // set new prev nodes
4963     for ( iNode = 0; iNode < nbNodes; iNode++ )
4964       prevNod[ iNode ] = nextNod[ iNode ];
4965
4966   } // loop on steps
4967 }
4968
4969 //=======================================================================
4970 /*!
4971  * \brief Create 1D and 2D elements around swept elements
4972  * \param mapNewNodes - source nodes and ones generated from them
4973  * \param newElemsMap - source elements and ones generated from them
4974  * \param elemNewNodesMap - nodes generated from each node of each element
4975  * \param elemSet - all swept elements
4976  * \param nbSteps - number of sweeping steps
4977  * \param srcElements - to append elem for each generated element
4978  */
4979 //=======================================================================
4980
4981 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4982                                   TTElemOfElemListMap &    newElemsMap,
4983                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4984                                   TIDSortedElemSet&        elemSet,
4985                                   const int                nbSteps,
4986                                   SMESH_SequenceOfElemPtr& srcElements)
4987 {
4988   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4989   SMESHDS_Mesh* aMesh = GetMeshDS();
4990
4991   // Find nodes belonging to only one initial element - sweep them into edges.
4992
4993   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4994   for ( ; nList != mapNewNodes.end(); nList++ )
4995   {
4996     const SMDS_MeshNode* node =
4997       static_cast<const SMDS_MeshNode*>( nList->first );
4998     if ( newElemsMap.count( node ))
4999       continue; // node was extruded into edge
5000     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5001     int nbInitElems = 0;
5002     const SMDS_MeshElement* el = 0;
5003     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5004     while ( eIt->more() && nbInitElems < 2 ) {
5005       const SMDS_MeshElement* e = eIt->next();
5006       SMDSAbs_ElementType type = e->GetType();
5007       if ( type == SMDSAbs_Volume || type < highType ) continue;
5008       if ( type > highType ) {
5009         nbInitElems = 0;
5010         highType = type;
5011       }
5012       el = e;
5013       nbInitElems += elemSet.count(el);
5014     }
5015     if ( nbInitElems < 2 ) {
5016       bool NotCreateEdge = el && el->IsMediumNode(node);
5017       if(!NotCreateEdge) {
5018         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5019         list<const SMDS_MeshElement*> newEdges;
5020         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5021       }
5022     }
5023   }
5024
5025   // Make a ceiling for each element ie an equal element of last new nodes.
5026   // Find free links of faces - make edges and sweep them into faces.
5027
5028   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5029
5030   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5031   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5032   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5033   {
5034     const SMDS_MeshElement* elem = itElem->first;
5035     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5036
5037     if(itElem->second.size()==0) continue;
5038
5039     const bool isQuadratic = elem->IsQuadratic();
5040
5041     if ( elem->GetType() == SMDSAbs_Edge ) {
5042       // create a ceiling edge
5043       if ( !isQuadratic ) {
5044         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5045                                vecNewNodes[ 1 ]->second.back())) {
5046           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5047                                                    vecNewNodes[ 1 ]->second.back()));
5048           srcElements.Append( elem );
5049         }
5050       }
5051       else {
5052         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5053                                vecNewNodes[ 1 ]->second.back(),
5054                                vecNewNodes[ 2 ]->second.back())) {
5055           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5056                                                    vecNewNodes[ 1 ]->second.back(),
5057                                                    vecNewNodes[ 2 ]->second.back()));
5058           srcElements.Append( elem );
5059         }
5060       }
5061     }
5062     if ( elem->GetType() != SMDSAbs_Face )
5063       continue;
5064
5065     bool hasFreeLinks = false;
5066
5067     TIDSortedElemSet avoidSet;
5068     avoidSet.insert( elem );
5069
5070     set<const SMDS_MeshNode*> aFaceLastNodes;
5071     int iNode, nbNodes = vecNewNodes.size();
5072     if ( !isQuadratic ) {
5073       // loop on the face nodes
5074       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5075         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5076         // look for free links of the face
5077         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5078         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5079         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5080         // check if a link n1-n2 is free
5081         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5082           hasFreeLinks = true;
5083           // make a new edge and a ceiling for a new edge
5084           const SMDS_MeshElement* edge;
5085           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5086             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5087             srcElements.Append( myLastCreatedElems.Last() );
5088           }
5089           n1 = vecNewNodes[ iNode ]->second.back();
5090           n2 = vecNewNodes[ iNext ]->second.back();
5091           if ( !aMesh->FindEdge( n1, n2 )) {
5092             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5093             srcElements.Append( edge );
5094           }
5095         }
5096       }
5097     }
5098     else { // elem is quadratic face
5099       int nbn = nbNodes/2;
5100       for ( iNode = 0; iNode < nbn; iNode++ ) {
5101         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5102         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5103         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5104         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5105         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5106         // check if a link is free
5107         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5108              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5109              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5110           hasFreeLinks = true;
5111           // make an edge and a ceiling for a new edge
5112           // find medium node
5113           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5114             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5115             srcElements.Append( elem );
5116           }
5117           n1 = vecNewNodes[ iNode ]->second.back();
5118           n2 = vecNewNodes[ iNext ]->second.back();
5119           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5120           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5121             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5122             srcElements.Append( elem );
5123           }
5124         }
5125       }
5126       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5127         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5128       }
5129     }
5130
5131     // sweep free links into faces
5132
5133     if ( hasFreeLinks ) {
5134       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5135       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5136
5137       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5138       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5139       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5140         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5141         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5142       }
5143       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5144         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5145         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5146       }
5147       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5148         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5149         std::advance( v, volNb );
5150         // find indices of free faces of a volume and their source edges
5151         list< int > freeInd;
5152         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5153         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5154         int iF, nbF = vTool.NbFaces();
5155         for ( iF = 0; iF < nbF; iF ++ ) {
5156           if (vTool.IsFreeFace( iF ) &&
5157               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5158               initNodeSet != faceNodeSet) // except an initial face
5159           {
5160             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5161               continue;
5162             if ( faceNodeSet == initNodeSetNoCenter )
5163               continue;
5164             freeInd.push_back( iF );
5165             // find source edge of a free face iF
5166             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5167             vector<const SMDS_MeshNode*>::iterator lastCommom;
5168             commonNodes.resize( nbNodes, 0 );
5169             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5170                                                 initNodeSet.begin(), initNodeSet.end(),
5171                                                 commonNodes.begin());
5172             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5173               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5174             else
5175               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5176 #ifdef _DEBUG_
5177             if ( !srcEdges.back() )
5178             {
5179               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5180                    << iF << " of volume #" << vTool.ID() << endl;
5181             }
5182 #endif
5183           }
5184         }
5185         if ( freeInd.empty() )
5186           continue;
5187
5188         // create wall faces for all steps;
5189         // if such a face has been already created by sweep of edge,
5190         // assure that its orientation is OK
5191         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5192         {
5193           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5194           vTool.SetExternalNormal();
5195           const int nextShift = vTool.IsForward() ? +1 : -1;
5196           list< int >::iterator ind = freeInd.begin();
5197           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5198           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5199           {
5200             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5201             int nbn = vTool.NbFaceNodes( *ind );
5202             const SMDS_MeshElement * f = 0;
5203             if ( nbn == 3 )              ///// triangle
5204             {
5205               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5206               if ( !f ||
5207                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208               {
5209                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5210                                                      nodes[ 1 ],
5211                                                      nodes[ 1 + nextShift ] };
5212                 if ( f )
5213                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5214                 else
5215                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5216                                                             newOrder[ 2 ] ));
5217               }
5218             }
5219             else if ( nbn == 4 )       ///// quadrangle
5220             {
5221               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5222               if ( !f ||
5223                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5224               {
5225                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5226                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5227                 if ( f )
5228                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5229                 else
5230                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5231                                                             newOrder[ 2 ], newOrder[ 3 ]));
5232               }
5233             }
5234             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5235             {
5236               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5237               if ( !f ||
5238                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5239               {
5240                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5241                                                      nodes[2],
5242                                                      nodes[2 + 2*nextShift],
5243                                                      nodes[3 - 2*nextShift],
5244                                                      nodes[3],
5245                                                      nodes[3 + 2*nextShift]};
5246                 if ( f )
5247                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5248                 else
5249                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5250                                                             newOrder[ 1 ],
5251                                                             newOrder[ 2 ],
5252                                                             newOrder[ 3 ],
5253                                                             newOrder[ 4 ],
5254                                                             newOrder[ 5 ] ));
5255               }
5256             }
5257             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5258             {
5259               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5260                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5261               if ( !f ||
5262                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5263               {
5264                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5265                                                      nodes[4 - 2*nextShift],
5266                                                      nodes[4],
5267                                                      nodes[4 + 2*nextShift],
5268                                                      nodes[1],
5269                                                      nodes[5 - 2*nextShift],
5270                                                      nodes[5],
5271                                                      nodes[5 + 2*nextShift] };
5272                 if ( f )
5273                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5274                 else
5275                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5276                                                            newOrder[ 2 ], newOrder[ 3 ],
5277                                                            newOrder[ 4 ], newOrder[ 5 ],
5278                                                            newOrder[ 6 ], newOrder[ 7 ]));
5279               }
5280             }
5281             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5282             {
5283               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5284                                       SMDSAbs_Face, /*noMedium=*/false);
5285               if ( !f ||
5286                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5287               {
5288                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5289                                                      nodes[4 - 2*nextShift],
5290                                                      nodes[4],
5291                                                      nodes[4 + 2*nextShift],
5292                                                      nodes[1],
5293                                                      nodes[5 - 2*nextShift],
5294                                                      nodes[5],
5295                                                      nodes[5 + 2*nextShift],
5296                                                      nodes[8] };
5297                 if ( f )
5298                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5299                 else
5300                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5301                                                            newOrder[ 2 ], newOrder[ 3 ],
5302                                                            newOrder[ 4 ], newOrder[ 5 ],
5303                                                            newOrder[ 6 ], newOrder[ 7 ],
5304                                                            newOrder[ 8 ]));
5305               }
5306             }
5307             else  //////// polygon
5308             {
5309               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5310               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5311               if ( !f ||
5312                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5313               {
5314                 if ( !vTool.IsForward() )
5315                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5316                 if ( f )
5317                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5318                 else
5319                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5320               }
5321             }
5322
5323             while ( srcElements.Length() < myLastCreatedElems.Length() )
5324               srcElements.Append( *srcEdge );
5325
5326           }  // loop on free faces
5327
5328           // go to the next volume
5329           iVol = 0;
5330           while ( iVol++ < nbVolumesByStep ) v++;
5331
5332         } // loop on steps
5333       } // loop on volumes of one step
5334     } // sweep free links into faces
5335
5336     // Make a ceiling face with a normal external to a volume
5337
5338     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5339     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5340     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5341
5342     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5343       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5344       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5345     }
5346     if ( iF >= 0 )
5347     {
5348       lastVol.SetExternalNormal();
5349       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5350       const               int nbn = lastVol.NbFaceNodes( iF );
5351       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5352       if ( !hasFreeLinks ||
5353            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5354       {
5355         const vector<int>& interlace =
5356           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5357         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5358
5359         AddElement( nodeVec, anyFace.Init( elem ));
5360
5361         while ( srcElements.Length() < myLastCreatedElems.Length() )
5362           srcElements.Append( elem );
5363       }
5364     }
5365   } // loop on swept elements
5366 }
5367
5368 //=======================================================================
5369 //function : RotationSweep
5370 //purpose  :
5371 //=======================================================================
5372
5373 SMESH_MeshEditor::PGroupIDs
5374 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5375                                 const gp_Ax1&      theAxis,
5376                                 const double       theAngle,
5377                                 const int          theNbSteps,
5378                                 const double       theTol,
5379                                 const bool         theMakeGroups,
5380                                 const bool         theMakeWalls)
5381 {
5382   myLastCreatedElems.Clear();
5383   myLastCreatedNodes.Clear();
5384
5385   // source elements for each generated one
5386   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5387
5388   MESSAGE( "RotationSweep()");
5389   gp_Trsf aTrsf;
5390   aTrsf.SetRotation( theAxis, theAngle );
5391   gp_Trsf aTrsf2;
5392   aTrsf2.SetRotation( theAxis, theAngle/2. );
5393
5394   gp_Lin aLine( theAxis );
5395   double aSqTol = theTol * theTol;
5396
5397   SMESHDS_Mesh* aMesh = GetMeshDS();
5398
5399   TNodeOfNodeListMap mapNewNodes;
5400   TElemOfVecOfNnlmiMap mapElemNewNodes;
5401   TTElemOfElemListMap newElemsMap;
5402
5403   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5404                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5405                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5406   // loop on theElemSets
5407   setElemsFirst( theElemSets );
5408   TIDSortedElemSet::iterator itElem;
5409   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5410   {
5411     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5412     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5413       const SMDS_MeshElement* elem = *itElem;
5414       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5415         continue;
5416       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5417       newNodesItVec.reserve( elem->NbNodes() );
5418
5419       // loop on elem nodes
5420       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5421       while ( itN->more() )
5422       {
5423         const SMDS_MeshNode* node = cast2Node( itN->next() );
5424
5425         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5426         double coord[3];
5427         aXYZ.Coord( coord[0], coord[1], coord[2] );
5428         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5429
5430         // check if a node has been already sweeped
5431         TNodeOfNodeListMapItr nIt =
5432           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5433         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5434         if ( listNewNodes.empty() )
5435         {
5436           // check if we are to create medium nodes between corner ones
5437           bool needMediumNodes = false;
5438           if ( isQuadraticMesh )
5439           {
5440             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5441             while (it->more() && !needMediumNodes )
5442             {
5443               const SMDS_MeshElement* invElem = it->next();
5444               if ( invElem != elem && !theElems.count( invElem )) continue;
5445               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5446               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5447                 needMediumNodes = true;
5448             }
5449           }
5450
5451           // make new nodes
5452           const SMDS_MeshNode * newNode = node;
5453           for ( int i = 0; i < theNbSteps; i++ ) {
5454             if ( !isOnAxis ) {
5455               if ( needMediumNodes )  // create a medium node
5456               {
5457                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5458                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5459                 myLastCreatedNodes.Append(newNode);
5460                 srcNodes.Append( node );
5461                 listNewNodes.push_back( newNode );
5462                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5463               }
5464               else {
5465                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5466               }
5467               // create a corner node
5468               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5469               myLastCreatedNodes.Append(newNode);
5470               srcNodes.Append( node );
5471               listNewNodes.push_back( newNode );
5472             }
5473             else {
5474               listNewNodes.push_back( newNode );
5475               // if ( needMediumNodes )
5476               //   listNewNodes.push_back( newNode );
5477             }
5478           }
5479         }
5480         newNodesItVec.push_back( nIt );
5481       }
5482       // make new elements
5483       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5484     }
5485   }
5486
5487   if ( theMakeWalls )
5488     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5489
5490   PGroupIDs newGroupIDs;
5491   if ( theMakeGroups )
5492     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5493
5494   return newGroupIDs;
5495 }
5496
5497 //=======================================================================
5498 //function : ExtrusParam
5499 //purpose  : standard construction
5500 //=======================================================================
5501
5502 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5503                                             const int      theNbSteps,
5504                                             const int      theFlags,
5505                                             const double   theTolerance):
5506   myDir( theStep ),
5507   myFlags( theFlags ),
5508   myTolerance( theTolerance ),
5509   myElemsToUse( NULL )
5510 {
5511   mySteps = new TColStd_HSequenceOfReal;
5512   const double stepSize = theStep.Magnitude();
5513   for (int i=1; i<=theNbSteps; i++ )
5514     mySteps->Append( stepSize );
5515
5516   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5517       ( theTolerance > 0 ))
5518   {
5519     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5520   }
5521   else
5522   {
5523     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5524   }
5525 }
5526
5527 //=======================================================================
5528 //function : ExtrusParam
5529 //purpose  : steps are given explicitly
5530 //=======================================================================
5531
5532 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5533                                             Handle(TColStd_HSequenceOfReal) theSteps,
5534                                             const int                       theFlags,
5535                                             const double                    theTolerance):
5536   myDir( theDir ),
5537   mySteps( theSteps ),
5538   myFlags( theFlags ),
5539   myTolerance( theTolerance ),
5540   myElemsToUse( NULL )
5541 {
5542   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5543       ( theTolerance > 0 ))
5544   {
5545     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5546   }
5547   else
5548   {
5549     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5550   }
5551 }
5552
5553 //=======================================================================
5554 //function : ExtrusParam
5555 //purpose  : for extrusion by normal
5556 //=======================================================================
5557
5558 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5559                                             const int    theNbSteps,
5560                                             const int    theFlags,
5561                                             const int    theDim ):
5562   myDir( 1,0,0 ),
5563   mySteps( new TColStd_HSequenceOfReal ),
5564   myFlags( theFlags ),
5565   myTolerance( 0 ),
5566   myElemsToUse( NULL )
5567 {
5568   for (int i = 0; i < theNbSteps; i++ )
5569     mySteps->Append( theStepSize );
5570
5571   if ( theDim == 1 )
5572   {
5573     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5574   }
5575   else
5576   {
5577     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5578   }
5579 }
5580
5581 //=======================================================================
5582 //function : ExtrusParam::SetElementsToUse
5583 //purpose  : stores elements to use for extrusion by normal, depending on
5584 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5585 //=======================================================================
5586
5587 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5588 {
5589   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5590 }
5591
5592 //=======================================================================
5593 //function : ExtrusParam::beginStepIter
5594 //purpose  : prepare iteration on steps
5595 //=======================================================================
5596
5597 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5598 {
5599   myWithMediumNodes = withMediumNodes;
5600   myNextStep = 1;
5601   myCurSteps.clear();
5602 }
5603 //=======================================================================
5604 //function : ExtrusParam::moreSteps
5605 //purpose  : are there more steps?
5606 //=======================================================================
5607
5608 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5609 {
5610   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5611 }
5612 //=======================================================================
5613 //function : ExtrusParam::nextStep
5614 //purpose  : returns the next step
5615 //=======================================================================
5616
5617 double SMESH_MeshEditor::ExtrusParam::nextStep()
5618 {
5619   double res = 0;
5620   if ( !myCurSteps.empty() )
5621   {
5622     res = myCurSteps.back();
5623     myCurSteps.pop_back();
5624   }
5625   else if ( myNextStep <= mySteps->Length() )
5626   {
5627     myCurSteps.push_back( mySteps->Value( myNextStep ));
5628     ++myNextStep;
5629     if ( myWithMediumNodes )
5630     {
5631       myCurSteps.back() /= 2.;
5632       myCurSteps.push_back( myCurSteps.back() );
5633     }
5634     res = nextStep();
5635   }
5636   return res;
5637 }
5638
5639 //=======================================================================
5640 //function : ExtrusParam::makeNodesByDir
5641 //purpose  : create nodes for standard extrusion
5642 //=======================================================================
5643
5644 int SMESH_MeshEditor::ExtrusParam::
5645 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5646                 const SMDS_MeshNode*              srcNode,
5647                 std::list<const SMDS_MeshNode*> & newNodes,
5648                 const bool                        makeMediumNodes)
5649 {
5650   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5651
5652   int nbNodes = 0;
5653   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5654   {
5655     p += myDir.XYZ() * nextStep();
5656     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5657     newNodes.push_back( newNode );
5658   }
5659   return nbNodes;
5660 }
5661
5662 //=======================================================================
5663 //function : ExtrusParam::makeNodesByDirAndSew
5664 //purpose  : create nodes for standard extrusion with sewing
5665 //=======================================================================
5666
5667 int SMESH_MeshEditor::ExtrusParam::
5668 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5669                       const SMDS_MeshNode*              srcNode,
5670                       std::list<const SMDS_MeshNode*> & newNodes,
5671                       const bool                        makeMediumNodes)
5672 {
5673   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5674
5675   int nbNodes = 0;
5676   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5677   {
5678     P1 += myDir.XYZ() * nextStep();
5679
5680     // try to search in sequence of existing nodes
5681     // if myNodes.Length()>0 we 'nave to use given sequence
5682     // else - use all nodes of mesh
5683     const SMDS_MeshNode * node = 0;
5684     if ( myNodes.Length() > 0 ) {
5685       int i;
5686       for(i=1; i<=myNodes.Length(); i++) {
5687         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5688         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5689         {
5690           node = myNodes.Value(i);
5691           break;
5692         }
5693       }
5694     }
5695     else {
5696       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5697       while(itn->more()) {
5698         SMESH_TNodeXYZ P2( itn->next() );
5699         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5700         {
5701           node = P2._node;
5702           break;
5703         }
5704       }
5705     }
5706
5707     if ( !node )
5708       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5709
5710     newNodes.push_back( node );
5711
5712   } // loop on steps
5713
5714   return nbNodes;
5715 }
5716
5717 //=======================================================================
5718 //function : ExtrusParam::makeNodesByNormal2D
5719 //purpose  : create nodes for extrusion using normals of faces
5720 //=======================================================================
5721
5722 int SMESH_MeshEditor::ExtrusParam::
5723 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5724                      const SMDS_MeshNode*              srcNode,
5725                      std::list<const SMDS_MeshNode*> & newNodes,
5726                      const bool                        makeMediumNodes)
5727 {
5728   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5729
5730   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5731
5732   // get normals to faces sharing srcNode
5733   vector< gp_XYZ > norms, baryCenters;
5734   gp_XYZ norm, avgNorm( 0,0,0 );
5735   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5736   while ( faceIt->more() )
5737   {
5738     const SMDS_MeshElement* face = faceIt->next();
5739     if ( myElemsToUse && !myElemsToUse->count( face ))
5740       continue;
5741     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5742     {
5743       norms.push_back( norm );
5744       avgNorm += norm;
5745       if ( !alongAvgNorm )
5746       {
5747         gp_XYZ bc(0,0,0);
5748         int nbN = 0;
5749         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5750           bc += SMESH_TNodeXYZ( nIt->next() );
5751         baryCenters.push_back( bc / nbN );
5752       }
5753     }
5754   }
5755
5756   if ( norms.empty() ) return 0;
5757
5758   double normSize = avgNorm.Modulus();
5759   if ( normSize < std::numeric_limits<double>::min() )
5760     return 0;
5761
5762   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5763   {
5764     myDir = avgNorm;
5765     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5766   }
5767
5768   avgNorm /= normSize;
5769
5770   int nbNodes = 0;
5771   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5772   {
5773     gp_XYZ pNew = p;
5774     double stepSize = nextStep();
5775
5776     if ( norms.size() > 1 )
5777     {
5778       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5779       {
5780         // translate plane of a face
5781         baryCenters[ iF ] += norms[ iF ] * stepSize;
5782
5783         // find point of intersection of the face plane located at baryCenters[ iF ]
5784         // and avgNorm located at pNew
5785         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5786         double dot  = ( norms[ iF ] * avgNorm );
5787         if ( dot < std::numeric_limits<double>::min() )
5788           dot = stepSize * 1e-3;
5789         double step = -( norms[ iF ] * pNew + d ) / dot;
5790         pNew += step * avgNorm;
5791       }
5792     }
5793     else
5794     {
5795       pNew += stepSize * avgNorm;
5796     }
5797     p = pNew;
5798
5799     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5800     newNodes.push_back( newNode );
5801   }
5802   return nbNodes;
5803 }
5804
5805 //=======================================================================
5806 //function : ExtrusParam::makeNodesByNormal1D
5807 //purpose  : create nodes for extrusion using normals of edges
5808 //=======================================================================
5809
5810 int SMESH_MeshEditor::ExtrusParam::
5811 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5812                      const SMDS_MeshNode*              srcNode,
5813                      std::list<const SMDS_MeshNode*> & newNodes,
5814                      const bool                        makeMediumNodes)
5815 {
5816   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5817   return 0;
5818 }
5819
5820 //=======================================================================
5821 //function : ExtrusionSweep
5822 //purpose  :
5823 //=======================================================================
5824
5825 SMESH_MeshEditor::PGroupIDs
5826 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5827                                   const gp_Vec&        theStep,
5828                                   const int            theNbSteps,
5829                                   TTElemOfElemListMap& newElemsMap,
5830                                   const int            theFlags,
5831                                   const double         theTolerance)
5832 {
5833   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5834   return ExtrusionSweep( theElems, aParams, newElemsMap );
5835 }
5836
5837
5838 //=======================================================================
5839 //function : ExtrusionSweep
5840 //purpose  :
5841 //=======================================================================
5842
5843 SMESH_MeshEditor::PGroupIDs
5844 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5845                                   ExtrusParam&         theParams,
5846                                   TTElemOfElemListMap& newElemsMap)
5847 {
5848   myLastCreatedElems.Clear();
5849   myLastCreatedNodes.Clear();
5850
5851   // source elements for each generated one
5852   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5853
5854   SMESHDS_Mesh* aMesh = GetMeshDS();
5855
5856   setElemsFirst( theElemSets );
5857   const int nbSteps = theParams.NbSteps();
5858   theParams.SetElementsToUse( theElemSets[0] );
5859
5860   TNodeOfNodeListMap mapNewNodes;
5861   //TNodeOfNodeVecMap mapNewNodes;
5862   TElemOfVecOfNnlmiMap mapElemNewNodes;
5863   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5864
5865   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5866                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5867                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5868   // loop on theElems
5869   TIDSortedElemSet::iterator itElem;
5870   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5871   {
5872     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5873     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5874     {
5875       // check element type
5876       const SMDS_MeshElement* elem = *itElem;
5877       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5878         continue;
5879
5880       const size_t nbNodes = elem->NbNodes();
5881       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5882       newNodesItVec.reserve( nbNodes );
5883
5884       // loop on elem nodes
5885       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5886       while ( itN->more() )
5887       {
5888         // check if a node has been already sweeped
5889         const SMDS_MeshNode* node = cast2Node( itN->next() );
5890         TNodeOfNodeListMap::iterator nIt =
5891           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5892         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5893         if ( listNewNodes.empty() )
5894         {
5895           // make new nodes
5896
5897           // check if we are to create medium nodes between corner ones
5898           bool needMediumNodes = false;
5899           if ( isQuadraticMesh )
5900           {
5901             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5902             while (it->more() && !needMediumNodes )
5903             {
5904               const SMDS_MeshElement* invElem = it->next();
5905               if ( invElem != elem && !theElems.count( invElem )) continue;
5906               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5907               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5908                 needMediumNodes = true;
5909             }
5910           }
5911           // create nodes for all steps
5912           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5913           {
5914             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5915             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5916             {
5917               myLastCreatedNodes.Append( *newNodesIt );
5918               srcNodes.Append( node );
5919             }
5920           }
5921           else
5922           {
5923             break; // newNodesItVec will be shorter than nbNodes
5924           }
5925         }
5926         newNodesItVec.push_back( nIt );
5927       }
5928       // make new elements
5929       if ( newNodesItVec.size() == nbNodes )
5930         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5931     }
5932   }
5933
5934   if ( theParams.ToMakeBoundary() ) {
5935     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5936   }
5937   PGroupIDs newGroupIDs;
5938   if ( theParams.ToMakeGroups() )
5939     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5940
5941   return newGroupIDs;
5942 }
5943
5944 //=======================================================================
5945 //function : ExtrusionAlongTrack
5946 //purpose  :
5947 //=======================================================================
5948 SMESH_MeshEditor::Extrusion_Error
5949 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5950                                        SMESH_subMesh*       theTrack,
5951                                        const SMDS_MeshNode* theN1,
5952                                        const bool           theHasAngles,
5953                                        list<double>&        theAngles,
5954                                        const bool           theLinearVariation,
5955                                        const bool           theHasRefPoint,
5956                                        const gp_Pnt&        theRefPoint,
5957                                        const bool           theMakeGroups)
5958 {
5959   MESSAGE("ExtrusionAlongTrack");
5960   myLastCreatedElems.Clear();
5961   myLastCreatedNodes.Clear();
5962
5963   int aNbE;
5964   std::list<double> aPrms;
5965   TIDSortedElemSet::iterator itElem;
5966
5967   gp_XYZ aGC;
5968   TopoDS_Edge aTrackEdge;
5969   TopoDS_Vertex aV1, aV2;
5970
5971   SMDS_ElemIteratorPtr aItE;
5972   SMDS_NodeIteratorPtr aItN;
5973   SMDSAbs_ElementType aTypeE;
5974
5975   TNodeOfNodeListMap mapNewNodes;
5976
5977   // 1. Check data
5978   aNbE = theElements[0].size() + theElements[1].size();
5979   // nothing to do
5980   if ( !aNbE )
5981     return EXTR_NO_ELEMENTS;
5982
5983   // 1.1 Track Pattern
5984   ASSERT( theTrack );
5985
5986   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5987
5988   aItE = pSubMeshDS->GetElements();
5989   while ( aItE->more() ) {
5990     const SMDS_MeshElement* pE = aItE->next();
5991     aTypeE = pE->GetType();
5992     // Pattern must contain links only
5993     if ( aTypeE != SMDSAbs_Edge )
5994       return EXTR_PATH_NOT_EDGE;
5995   }
5996
5997   list<SMESH_MeshEditor_PathPoint> fullList;
5998
5999   const TopoDS_Shape& aS = theTrack->GetSubShape();
6000   // Sub-shape for the Pattern must be an Edge or Wire
6001   if( aS.ShapeType() == TopAbs_EDGE ) {
6002     aTrackEdge = TopoDS::Edge( aS );
6003     // the Edge must not be degenerated
6004     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6005       return EXTR_BAD_PATH_SHAPE;
6006     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6007     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6008     const SMDS_MeshNode* aN1 = aItN->next();
6009     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6010     const SMDS_MeshNode* aN2 = aItN->next();
6011     // starting node must be aN1 or aN2
6012     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6013       return EXTR_BAD_STARTING_NODE;
6014     aItN = pSubMeshDS->GetNodes();
6015     while ( aItN->more() ) {
6016       const SMDS_MeshNode* pNode = aItN->next();
6017       const SMDS_EdgePosition* pEPos =
6018         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6019       double aT = pEPos->GetUParameter();
6020       aPrms.push_back( aT );
6021     }
6022     //Extrusion_Error err =
6023     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6024   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6025     list< SMESH_subMesh* > LSM;
6026     TopTools_SequenceOfShape Edges;
6027     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6028     while(itSM->more()) {
6029       SMESH_subMesh* SM = itSM->next();
6030       LSM.push_back(SM);
6031       const TopoDS_Shape& aS = SM->GetSubShape();
6032       Edges.Append(aS);
6033     }
6034     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6035     int startNid = theN1->GetID();
6036     TColStd_MapOfInteger UsedNums;
6037
6038     int NbEdges = Edges.Length();
6039     int i = 1;
6040     for(; i<=NbEdges; i++) {
6041       int k = 0;
6042       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6043       for(; itLSM!=LSM.end(); itLSM++) {
6044         k++;
6045         if(UsedNums.Contains(k)) continue;
6046         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6047         SMESH_subMesh* locTrack = *itLSM;
6048         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6049         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6050         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6051         const SMDS_MeshNode* aN1 = aItN->next();
6052         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6053         const SMDS_MeshNode* aN2 = aItN->next();
6054         // starting node must be aN1 or aN2
6055         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6056         // 2. Collect parameters on the track edge
6057         aPrms.clear();
6058         aItN = locMeshDS->GetNodes();
6059         while ( aItN->more() ) {
6060           const SMDS_MeshNode* pNode = aItN->next();
6061           const SMDS_EdgePosition* pEPos =
6062             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6063           double aT = pEPos->GetUParameter();
6064           aPrms.push_back( aT );
6065         }
6066         list<SMESH_MeshEditor_PathPoint> LPP;
6067         //Extrusion_Error err =
6068         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6069         LLPPs.push_back(LPP);
6070         UsedNums.Add(k);
6071         // update startN for search following egde
6072         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6073         else startNid = aN1->GetID();
6074         break;
6075       }
6076     }
6077     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6078     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6079     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6080     for(; itPP!=firstList.end(); itPP++) {
6081       fullList.push_back( *itPP );
6082     }
6083     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6084     fullList.pop_back();
6085     itLLPP++;
6086     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6087       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6088       itPP = currList.begin();
6089       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6090       gp_Dir D1 = PP1.Tangent();
6091       gp_Dir D2 = PP2.Tangent();
6092       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6093                            (D1.Z()+D2.Z())/2 ) );
6094       PP1.SetTangent(Dnew);
6095       fullList.push_back(PP1);
6096       itPP++;
6097       for(; itPP!=firstList.end(); itPP++) {
6098         fullList.push_back( *itPP );
6099       }
6100       PP1 = fullList.back();
6101       fullList.pop_back();
6102     }
6103     // if wire not closed
6104     fullList.push_back(PP1);
6105     // else ???
6106   }
6107   else {
6108     return EXTR_BAD_PATH_SHAPE;
6109   }
6110
6111   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6112                           theHasRefPoint, theRefPoint, theMakeGroups);
6113 }
6114
6115
6116 //=======================================================================
6117 //function : ExtrusionAlongTrack
6118 //purpose  :
6119 //=======================================================================
6120 SMESH_MeshEditor::Extrusion_Error
6121 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6122                                        SMESH_Mesh*          theTrack,
6123                                        const SMDS_MeshNode* theN1,
6124                                        const bool           theHasAngles,
6125                                        list<double>&        theAngles,
6126                                        const bool           theLinearVariation,
6127                                        const bool           theHasRefPoint,
6128                                        const gp_Pnt&        theRefPoint,
6129                                        const bool           theMakeGroups)
6130 {
6131   myLastCreatedElems.Clear();
6132   myLastCreatedNodes.Clear();
6133
6134   int aNbE;
6135   std::list<double> aPrms;
6136   TIDSortedElemSet::iterator itElem;
6137
6138   gp_XYZ aGC;
6139   TopoDS_Edge aTrackEdge;
6140   TopoDS_Vertex aV1, aV2;
6141
6142   SMDS_ElemIteratorPtr aItE;
6143   SMDS_NodeIteratorPtr aItN;
6144   SMDSAbs_ElementType aTypeE;
6145
6146   TNodeOfNodeListMap mapNewNodes;
6147
6148   // 1. Check data
6149   aNbE = theElements[0].size() + theElements[1].size();
6150   // nothing to do
6151   if ( !aNbE )
6152     return EXTR_NO_ELEMENTS;
6153
6154   // 1.1 Track Pattern
6155   ASSERT( theTrack );
6156
6157   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6158
6159   aItE = pMeshDS->elementsIterator();
6160   while ( aItE->more() ) {
6161     const SMDS_MeshElement* pE = aItE->next();
6162     aTypeE = pE->GetType();
6163     // Pattern must contain links only
6164     if ( aTypeE != SMDSAbs_Edge )
6165       return EXTR_PATH_NOT_EDGE;
6166   }
6167
6168   list<SMESH_MeshEditor_PathPoint> fullList;
6169
6170   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6171
6172   if ( !theTrack->HasShapeToMesh() ) {
6173     //Mesh without shape
6174     const SMDS_MeshNode* currentNode = NULL;
6175     const SMDS_MeshNode* prevNode = theN1;
6176     std::vector<const SMDS_MeshNode*> aNodesList;
6177     aNodesList.push_back(theN1);
6178     int nbEdges = 0, conn=0;
6179     const SMDS_MeshElement* prevElem = NULL;
6180     const SMDS_MeshElement* currentElem = NULL;
6181     int totalNbEdges = theTrack->NbEdges();
6182     SMDS_ElemIteratorPtr nIt;
6183
6184     //check start node
6185     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6186       return EXTR_BAD_STARTING_NODE;
6187     }
6188
6189     conn = nbEdgeConnectivity(theN1);
6190     if( conn != 1 )
6191       return EXTR_PATH_NOT_EDGE;
6192
6193     aItE = theN1->GetInverseElementIterator();
6194     prevElem = aItE->next();
6195     currentElem = prevElem;
6196     //Get all nodes
6197     if(totalNbEdges == 1 ) {
6198       nIt = currentElem->nodesIterator();
6199       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6200       if(currentNode == prevNode)
6201         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6202       aNodesList.push_back(currentNode);
6203     } else {
6204       nIt = currentElem->nodesIterator();
6205       while( nIt->more() ) {
6206         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6207         if(currentNode == prevNode)
6208           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6209         aNodesList.push_back(currentNode);
6210
6211         //case of the closed mesh
6212         if(currentNode == theN1) {
6213           nbEdges++;
6214           break;
6215         }
6216
6217         conn = nbEdgeConnectivity(currentNode);
6218         if(conn > 2) {
6219           return EXTR_PATH_NOT_EDGE;
6220         }else if( conn == 1 && nbEdges > 0 ) {
6221           //End of the path
6222           nbEdges++;
6223           break;
6224         }else {
6225           prevNode = currentNode;
6226           aItE = currentNode->GetInverseElementIterator();
6227           currentElem = aItE->next();
6228           if( currentElem  == prevElem)
6229             currentElem = aItE->next();
6230           nIt = currentElem->nodesIterator();
6231           prevElem = currentElem;
6232           nbEdges++;
6233         }
6234       }
6235     }
6236
6237     if(nbEdges != totalNbEdges)
6238       return EXTR_PATH_NOT_EDGE;
6239
6240     TopTools_SequenceOfShape Edges;
6241     double x1,x2,y1,y2,z1,z2;
6242     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6243     int startNid = theN1->GetID();
6244     for(int i = 1; i < aNodesList.size(); i++) {
6245       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6246       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6247       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6248       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6249       list<SMESH_MeshEditor_PathPoint> LPP;
6250       aPrms.clear();
6251       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6252       LLPPs.push_back(LPP);
6253       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6254       else startNid = aNodesList[i-1]->GetID();
6255
6256     }
6257
6258     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6259     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6260     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6261     for(; itPP!=firstList.end(); itPP++) {
6262       fullList.push_back( *itPP );
6263     }
6264
6265     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6266     SMESH_MeshEditor_PathPoint PP2;
6267     fullList.pop_back();
6268     itLLPP++;
6269     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6270       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6271       itPP = currList.begin();
6272       PP2 = currList.front();
6273       gp_Dir D1 = PP1.Tangent();
6274       gp_Dir D2 = PP2.Tangent();
6275       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6276                            (D1.Z()+D2.Z())/2 ) );
6277       PP1.SetTangent(Dnew);
6278       fullList.push_back(PP1);
6279       itPP++;
6280       for(; itPP!=currList.end(); itPP++) {
6281         fullList.push_back( *itPP );
6282       }
6283       PP1 = fullList.back();
6284       fullList.pop_back();
6285     }
6286     fullList.push_back(PP1);
6287
6288   } // Sub-shape for the Pattern must be an Edge or Wire
6289   else if( aS.ShapeType() == TopAbs_EDGE ) {
6290     aTrackEdge = TopoDS::Edge( aS );
6291     // the Edge must not be degenerated
6292     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6293       return EXTR_BAD_PATH_SHAPE;
6294     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6295     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6296     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6297     // starting node must be aN1 or aN2
6298     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6299       return EXTR_BAD_STARTING_NODE;
6300     aItN = pMeshDS->nodesIterator();
6301     while ( aItN->more() ) {
6302       const SMDS_MeshNode* pNode = aItN->next();
6303       if( pNode==aN1 || pNode==aN2 ) continue;
6304       const SMDS_EdgePosition* pEPos =
6305         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6306       double aT = pEPos->GetUParameter();
6307       aPrms.push_back( aT );
6308     }
6309     //Extrusion_Error err =
6310     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6311   }
6312   else if( aS.ShapeType() == TopAbs_WIRE ) {
6313     list< SMESH_subMesh* > LSM;
6314     TopTools_SequenceOfShape Edges;
6315     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6316     for(; eExp.More(); eExp.Next()) {
6317       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6318       if( SMESH_Algo::isDegenerated(E) ) continue;
6319       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6320       if(SM) {
6321         LSM.push_back(SM);
6322         Edges.Append(E);
6323       }
6324     }
6325     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6326     TopoDS_Vertex aVprev;
6327     TColStd_MapOfInteger UsedNums;
6328     int NbEdges = Edges.Length();
6329     int i = 1;
6330     for(; i<=NbEdges; i++) {
6331       int k = 0;
6332       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6333       for(; itLSM!=LSM.end(); itLSM++) {
6334         k++;
6335         if(UsedNums.Contains(k)) continue;
6336         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6337         SMESH_subMesh* locTrack = *itLSM;
6338         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6339         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6340         bool aN1isOK = false, aN2isOK = false;
6341         if ( aVprev.IsNull() ) {
6342           // if previous vertex is not yet defined, it means that we in the beginning of wire
6343           // and we have to find initial vertex corresponding to starting node theN1
6344           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6345           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6346           // starting node must be aN1 or aN2
6347           aN1isOK = ( aN1 && aN1 == theN1 );
6348           aN2isOK = ( aN2 && aN2 == theN1 );
6349         }
6350         else {
6351           // we have specified ending vertex of the previous edge on the previous iteration
6352           // and we have just to check that it corresponds to any vertex in current segment
6353           aN1isOK = aVprev.IsSame( aV1 );
6354           aN2isOK = aVprev.IsSame( aV2 );
6355         }
6356         if ( !aN1isOK && !aN2isOK ) continue;
6357         // 2. Collect parameters on the track edge
6358         aPrms.clear();
6359         aItN = locMeshDS->GetNodes();
6360         while ( aItN->more() ) {
6361           const SMDS_MeshNode*     pNode = aItN->next();
6362           const SMDS_EdgePosition* pEPos =
6363             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6364           double aT = pEPos->GetUParameter();
6365           aPrms.push_back( aT );
6366         }
6367         list<SMESH_MeshEditor_PathPoint> LPP;
6368         //Extrusion_Error err =
6369         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6370         LLPPs.push_back(LPP);
6371         UsedNums.Add(k);
6372         // update startN for search following egde
6373         if ( aN1isOK ) aVprev = aV2;
6374         else           aVprev = aV1;
6375         break;
6376       }
6377     }
6378     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6379     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6380     fullList.splice( fullList.end(), firstList );
6381
6382     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6383     fullList.pop_back();
6384     itLLPP++;
6385     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6386       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6387       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6388       gp_Dir D1 = PP1.Tangent();
6389       gp_Dir D2 = PP2.Tangent();
6390       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6391       PP1.SetTangent(Dnew);
6392       fullList.push_back(PP1);
6393       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6394       PP1 = fullList.back();
6395       fullList.pop_back();
6396     }
6397     // if wire not closed
6398     fullList.push_back(PP1);
6399     // else ???
6400   }
6401   else {
6402     return EXTR_BAD_PATH_SHAPE;
6403   }
6404
6405   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6406                           theHasRefPoint, theRefPoint, theMakeGroups);
6407 }
6408
6409
6410 //=======================================================================
6411 //function : MakeEdgePathPoints
6412 //purpose  : auxilary for ExtrusionAlongTrack
6413 //=======================================================================
6414 SMESH_MeshEditor::Extrusion_Error
6415 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6416                                      const TopoDS_Edge&                aTrackEdge,
6417                                      bool                              FirstIsStart,
6418                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6419 {
6420   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6421   aTolVec=1.e-7;
6422   aTolVec2=aTolVec*aTolVec;
6423   double aT1, aT2;
6424   TopoDS_Vertex aV1, aV2;
6425   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6426   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6427   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6428   // 2. Collect parameters on the track edge
6429   aPrms.push_front( aT1 );
6430   aPrms.push_back( aT2 );
6431   // sort parameters
6432   aPrms.sort();
6433   if( FirstIsStart ) {
6434     if ( aT1 > aT2 ) {
6435       aPrms.reverse();
6436     }
6437   }
6438   else {
6439     if ( aT2 > aT1 ) {
6440       aPrms.reverse();
6441     }
6442   }
6443   // 3. Path Points
6444   SMESH_MeshEditor_PathPoint aPP;
6445   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6446   std::list<double>::iterator aItD = aPrms.begin();
6447   for(; aItD != aPrms.end(); ++aItD) {
6448     double aT = *aItD;
6449     gp_Pnt aP3D;
6450     gp_Vec aVec;
6451     aC3D->D1( aT, aP3D, aVec );
6452     aL2 = aVec.SquareMagnitude();
6453     if ( aL2 < aTolVec2 )
6454       return EXTR_CANT_GET_TANGENT;
6455     gp_Dir aTgt( aVec );
6456     aPP.SetPnt( aP3D );
6457     aPP.SetTangent( aTgt );
6458     aPP.SetParameter( aT );
6459     LPP.push_back(aPP);
6460   }
6461   return EXTR_OK;
6462 }
6463
6464
6465 //=======================================================================
6466 //function : MakeExtrElements
6467 //purpose  : auxilary for ExtrusionAlongTrack
6468 //=======================================================================
6469 SMESH_MeshEditor::Extrusion_Error
6470 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6471                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6472                                    const bool                        theHasAngles,
6473                                    list<double>&                     theAngles,
6474                                    const bool                        theLinearVariation,
6475                                    const bool                        theHasRefPoint,
6476                                    const gp_Pnt&                     theRefPoint,
6477                                    const bool                        theMakeGroups)
6478 {
6479   const int aNbTP = fullList.size();
6480   // Angles
6481   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6482     LinearAngleVariation(aNbTP-1, theAngles);
6483   // fill vector of path points with angles
6484   vector<SMESH_MeshEditor_PathPoint> aPPs;
6485   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6486   list<double>::iterator                 itAngles = theAngles.begin();
6487   aPPs.push_back( *itPP++ );
6488   for( ; itPP != fullList.end(); itPP++) {
6489     aPPs.push_back( *itPP );
6490     if ( theHasAngles && itAngles != theAngles.end() )
6491       aPPs.back().SetAngle( *itAngles++ );
6492   }
6493
6494   TNodeOfNodeListMap   mapNewNodes;
6495   TElemOfVecOfNnlmiMap mapElemNewNodes;
6496   TTElemOfElemListMap  newElemsMap;
6497   TIDSortedElemSet::iterator itElem;
6498   // source elements for each generated one
6499   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6500
6501   // 3. Center of rotation aV0
6502   gp_Pnt aV0 = theRefPoint;
6503   if ( !theHasRefPoint )
6504   {
6505     gp_XYZ aGC( 0.,0.,0. );
6506     TIDSortedElemSet newNodes;
6507
6508     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6509     {
6510       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6511       itElem = theElements.begin();
6512       for ( ; itElem != theElements.end(); itElem++ ) {
6513         const SMDS_MeshElement* elem = *itElem;
6514
6515         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6516         while ( itN->more() ) {
6517           const SMDS_MeshElement* node = itN->next();
6518           if ( newNodes.insert( node ).second )
6519             aGC += SMESH_TNodeXYZ( node );
6520         }
6521       }
6522     }
6523     aGC /= newNodes.size();
6524     aV0.SetXYZ( aGC );
6525   } // if (!theHasRefPoint) {
6526
6527   // 4. Processing the elements
6528   SMESHDS_Mesh* aMesh = GetMeshDS();
6529
6530   setElemsFirst( theElemSets );
6531   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6532   {
6533     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6534     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6535       // check element type
6536       const SMDS_MeshElement* elem = *itElem;
6537       if ( !elem )
6538         continue;
6539       // SMDSAbs_ElementType aTypeE = elem->GetType();
6540       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6541       //   continue;
6542
6543       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6544       newNodesItVec.reserve( elem->NbNodes() );
6545
6546       // loop on elem nodes
6547       int nodeIndex = -1;
6548       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6549       while ( itN->more() )
6550       {
6551         ++nodeIndex;
6552         // check if a node has been already processed
6553         const SMDS_MeshNode* node =
6554           static_cast<const SMDS_MeshNode*>( itN->next() );
6555         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6556         if ( nIt == mapNewNodes.end() ) {
6557           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6558           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6559
6560           // make new nodes
6561           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6562           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6563           gp_Ax1 anAx1, anAxT1T0;
6564           gp_Dir aDT1x, aDT0x, aDT1T0;
6565
6566           aTolAng=1.e-4;
6567
6568           aV0x = aV0;
6569           aPN0 = SMESH_TNodeXYZ( node );
6570
6571           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6572           aP0x = aPP0.Pnt();
6573           aDT0x= aPP0.Tangent();
6574           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6575
6576           for ( int j = 1; j < aNbTP; ++j ) {
6577             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6578             aP1x     = aPP1.Pnt();
6579             aDT1x    = aPP1.Tangent();
6580             aAngle1x = aPP1.Angle();
6581
6582             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6583             // Translation
6584             gp_Vec aV01x( aP0x, aP1x );
6585             aTrsf.SetTranslation( aV01x );
6586
6587             // traslated point
6588             aV1x = aV0x.Transformed( aTrsf );
6589             aPN1 = aPN0.Transformed( aTrsf );
6590
6591             // rotation 1 [ T1,T0 ]
6592             aAngleT1T0=-aDT1x.Angle( aDT0x );
6593             if (fabs(aAngleT1T0) > aTolAng) {
6594               aDT1T0=aDT1x^aDT0x;
6595               anAxT1T0.SetLocation( aV1x );
6596               anAxT1T0.SetDirection( aDT1T0 );
6597               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6598
6599               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6600             }
6601
6602             // rotation 2
6603             if ( theHasAngles ) {
6604               anAx1.SetLocation( aV1x );
6605               anAx1.SetDirection( aDT1x );
6606               aTrsfRot.SetRotation( anAx1, aAngle1x );
6607
6608               aPN1 = aPN1.Transformed( aTrsfRot );
6609             }
6610
6611             // make new node
6612             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6613             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6614               // create additional node
6615               double x = ( aPN1.X() + aPN0.X() )/2.;
6616               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6617               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6618               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6619               myLastCreatedNodes.Append(newNode);
6620               srcNodes.Append( node );
6621               listNewNodes.push_back( newNode );
6622             }
6623             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6624             myLastCreatedNodes.Append(newNode);
6625             srcNodes.Append( node );
6626             listNewNodes.push_back( newNode );
6627
6628             aPN0 = aPN1;
6629             aP0x = aP1x;
6630             aV0x = aV1x;
6631             aDT0x = aDT1x;
6632           }
6633         }
6634
6635         else {
6636           // if current elem is quadratic and current node is not medium
6637           // we have to check - may be it is needed to insert additional nodes
6638           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6639             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6640             if(listNewNodes.size()==aNbTP-1) {
6641               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6642               gp_XYZ P(node->X(), node->Y(), node->Z());
6643               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6644               int i;
6645               for(i=0; i<aNbTP-1; i++) {
6646                 const SMDS_MeshNode* N = *it;
6647                 double x = ( N->X() + P.X() )/2.;
6648                 double y = ( N->Y() + P.Y() )/2.;
6649                 double z = ( N->Z() + P.Z() )/2.;
6650                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6651                 srcNodes.Append( node );
6652                 myLastCreatedNodes.Append(newN);
6653                 aNodes[2*i] = newN;
6654                 aNodes[2*i+1] = N;
6655                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6656               }
6657               listNewNodes.clear();
6658               for(i=0; i<2*(aNbTP-1); i++) {
6659                 listNewNodes.push_back(aNodes[i]);
6660               }
6661             }
6662           }
6663         }
6664
6665         newNodesItVec.push_back( nIt );
6666       }
6667       // make new elements
6668       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6669       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6670       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6671     }
6672   }
6673
6674   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6675
6676   if ( theMakeGroups )
6677     generateGroups( srcNodes, srcElems, "extruded");
6678
6679   return EXTR_OK;
6680 }
6681
6682
6683 //=======================================================================
6684 //function : LinearAngleVariation
6685 //purpose  : auxilary for ExtrusionAlongTrack
6686 //=======================================================================
6687 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6688                                             list<double>& Angles)
6689 {
6690   int nbAngles = Angles.size();
6691   if( nbSteps > nbAngles ) {
6692     vector<double> theAngles(nbAngles);
6693     list<double>::iterator it = Angles.begin();
6694     int i = -1;
6695     for(; it!=Angles.end(); it++) {
6696       i++;
6697       theAngles[i] = (*it);
6698     }
6699     list<double> res;
6700     double rAn2St = double( nbAngles ) / double( nbSteps );
6701     double angPrev = 0, angle;
6702     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6703       double angCur = rAn2St * ( iSt+1 );
6704       double angCurFloor  = floor( angCur );
6705       double angPrevFloor = floor( angPrev );
6706       if ( angPrevFloor == angCurFloor )
6707         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6708       else {
6709         int iP = int( angPrevFloor );
6710         double angPrevCeil = ceil(angPrev);
6711         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6712
6713         int iC = int( angCurFloor );
6714         if ( iC < nbAngles )
6715           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6716
6717         iP = int( angPrevCeil );
6718         while ( iC-- > iP )
6719           angle += theAngles[ iC ];
6720       }
6721       res.push_back(angle);
6722       angPrev = angCur;
6723     }
6724     Angles.clear();
6725     it = res.begin();
6726     for(; it!=res.end(); it++)
6727       Angles.push_back( *it );
6728   }
6729 }
6730
6731
6732 //================================================================================
6733 /*!
6734  * \brief Move or copy theElements applying theTrsf to their nodes
6735  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6736  *  \param theTrsf - transformation to apply
6737  *  \param theCopy - if true, create translated copies of theElems
6738  *  \param theMakeGroups - if true and theCopy, create translated groups
6739  *  \param theTargetMesh - mesh to copy translated elements into
6740  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6741  */
6742 //================================================================================
6743
6744 SMESH_MeshEditor::PGroupIDs
6745 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6746                              const gp_Trsf&     theTrsf,
6747                              const bool         theCopy,
6748                              const bool         theMakeGroups,
6749                              SMESH_Mesh*        theTargetMesh)
6750 {
6751   myLastCreatedElems.Clear();
6752   myLastCreatedNodes.Clear();
6753
6754   bool needReverse = false;
6755   string groupPostfix;
6756   switch ( theTrsf.Form() ) {
6757   case gp_PntMirror:
6758     MESSAGE("gp_PntMirror");
6759     needReverse = true;
6760     groupPostfix = "mirrored";
6761     break;
6762   case gp_Ax1Mirror:
6763     MESSAGE("gp_Ax1Mirror");
6764     groupPostfix = "mirrored";
6765     break;
6766   case gp_Ax2Mirror:
6767     MESSAGE("gp_Ax2Mirror");
6768     needReverse = true;
6769     groupPostfix = "mirrored";
6770     break;
6771   case gp_Rotation:
6772     MESSAGE("gp_Rotation");
6773     groupPostfix = "rotated";
6774     break;
6775   case gp_Translation:
6776     MESSAGE("gp_Translation");
6777     groupPostfix = "translated";
6778     break;
6779   case gp_Scale:
6780     MESSAGE("gp_Scale");
6781     groupPostfix = "scaled";
6782     break;
6783   case gp_CompoundTrsf: // different scale by axis
6784     MESSAGE("gp_CompoundTrsf");
6785     groupPostfix = "scaled";
6786     break;
6787   default:
6788     MESSAGE("default");
6789     needReverse = false;
6790     groupPostfix = "transformed";
6791   }
6792
6793   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6794   SMESHDS_Mesh* aMesh    = GetMeshDS();
6795
6796   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6797   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6798   SMESH_MeshEditor::ElemFeatures elemType;
6799
6800   // map old node to new one
6801   TNodeNodeMap nodeMap;
6802
6803   // elements sharing moved nodes; those of them which have all
6804   // nodes mirrored but are not in theElems are to be reversed
6805   TIDSortedElemSet inverseElemSet;
6806
6807   // source elements for each generated one
6808   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6809
6810   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6811   TIDSortedElemSet orphanNode;
6812
6813   if ( theElems.empty() ) // transform the whole mesh
6814   {
6815     // add all elements
6816     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6817     while ( eIt->more() ) theElems.insert( eIt->next() );
6818     // add orphan nodes
6819     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6820     while ( nIt->more() )
6821     {
6822       const SMDS_MeshNode* node = nIt->next();
6823       if ( node->NbInverseElements() == 0)
6824         orphanNode.insert( node );
6825     }
6826   }
6827
6828   // loop on elements to transform nodes : first orphan nodes then elems
6829   TIDSortedElemSet::iterator itElem;
6830   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6831   for (int i=0; i<2; i++)
6832     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6833     {
6834       const SMDS_MeshElement* elem = *itElem;
6835       if ( !elem )
6836         continue;
6837
6838       // loop on elem nodes
6839       double coord[3];
6840       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6841       while ( itN->more() )
6842       {
6843         const SMDS_MeshNode* node = cast2Node( itN->next() );
6844         // check if a node has been already transformed
6845         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6846           nodeMap.insert( make_pair ( node, node ));
6847         if ( !n2n_isnew.second )
6848           continue;
6849
6850         node->GetXYZ( coord );
6851         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6852         if ( theTargetMesh ) {
6853           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6854           n2n_isnew.first->second = newNode;
6855           myLastCreatedNodes.Append(newNode);
6856           srcNodes.Append( node );
6857         }
6858         else if ( theCopy ) {
6859           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6860           n2n_isnew.first->second = newNode;
6861           myLastCreatedNodes.Append(newNode);
6862           srcNodes.Append( node );
6863         }
6864         else {
6865           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6866           // node position on shape becomes invalid
6867           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6868             ( SMDS_SpacePosition::originSpacePosition() );
6869         }
6870
6871         // keep inverse elements
6872         if ( !theCopy && !theTargetMesh && needReverse ) {
6873           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6874           while ( invElemIt->more() ) {
6875             const SMDS_MeshElement* iel = invElemIt->next();
6876             inverseElemSet.insert( iel );
6877           }
6878         }
6879       }
6880     } // loop on elems in { &orphanNode, &theElems };
6881
6882   // either create new elements or reverse mirrored ones
6883   if ( !theCopy && !needReverse && !theTargetMesh )
6884     return PGroupIDs();
6885
6886   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6887
6888   // Replicate or reverse elements
6889
6890   std::vector<int> iForw;
6891   vector<const SMDS_MeshNode*> nodes;
6892   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6893   {
6894     const SMDS_MeshElement* elem = *itElem;
6895     if ( !elem ) continue;
6896
6897     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6898     int                  nbNodes  = elem->NbNodes();
6899     if ( geomType == SMDSGeom_NONE ) continue; // node
6900
6901     nodes.resize( nbNodes );
6902
6903     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6904     {
6905       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6906       if (!aPolyedre)
6907         continue;
6908       nodes.clear();
6909       bool allTransformed = true;
6910       int nbFaces = aPolyedre->NbFaces();
6911       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6912       {
6913         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6914         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6915         {
6916           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6917           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6918           if ( nodeMapIt == nodeMap.end() )
6919             allTransformed = false; // not all nodes transformed
6920           else
6921             nodes.push_back((*nodeMapIt).second);
6922         }
6923         if ( needReverse && allTransformed )
6924           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6925       }
6926       if ( !allTransformed )
6927         continue; // not all nodes transformed
6928     }
6929     else // ----------------------- the rest element types
6930     {
6931       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6932       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6933       const vector<int>&    i = needReverse ? iRev : iForw;
6934
6935       // find transformed nodes
6936       int iNode = 0;
6937       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6938       while ( itN->more() ) {
6939         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6940         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6941         if ( nodeMapIt == nodeMap.end() )
6942           break; // not all nodes transformed
6943         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6944       }
6945       if ( iNode != nbNodes )
6946         continue; // not all nodes transformed
6947     }
6948
6949     if ( editor ) {
6950       // copy in this or a new mesh
6951       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6952         srcElems.Append( elem );
6953     }
6954     else {
6955       // reverse element as it was reversed by transformation
6956       if ( nbNodes > 2 )
6957         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6958     }
6959
6960   } // loop on elements
6961
6962   if ( editor && editor != this )
6963     myLastCreatedElems = editor->myLastCreatedElems;
6964
6965   PGroupIDs newGroupIDs;
6966
6967   if ( ( theMakeGroups && theCopy ) ||
6968        ( theMakeGroups && theTargetMesh ) )
6969     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6970
6971   return newGroupIDs;
6972 }
6973
6974 //=======================================================================
6975 /*!
6976  * \brief Create groups of elements made during transformation
6977  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6978  *  \param elemGens - elements making corresponding myLastCreatedElems
6979  *  \param postfix - to append to names of new groups
6980  *  \param targetMesh - mesh to create groups in
6981  *  \param topPresent - is there "top" elements that are created by sweeping
6982  */
6983 //=======================================================================
6984
6985 SMESH_MeshEditor::PGroupIDs
6986 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6987                                  const SMESH_SequenceOfElemPtr& elemGens,
6988                                  const std::string&             postfix,
6989                                  SMESH_Mesh*                    targetMesh,
6990                                  const bool                     topPresent)
6991 {
6992   PGroupIDs newGroupIDs( new list<int> );
6993   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6994
6995   // Sort existing groups by types and collect their names
6996
6997   // containers to store an old group and generated new ones;
6998   // 1st new group is for result elems of different type than a source one;
6999   // 2nd new group is for same type result elems ("top" group at extrusion)
7000   using boost::tuple;
7001   using boost::make_tuple;
7002   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7003   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7004   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7005   // group names
7006   set< string > groupNames;
7007
7008   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7009   if ( !groupIt->more() ) return newGroupIDs;
7010
7011   int newGroupID = mesh->GetGroupIds().back()+1;
7012   while ( groupIt->more() )
7013   {
7014     SMESH_Group * group = groupIt->next();
7015     if ( !group ) continue;
7016     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7017     if ( !groupDS || groupDS->IsEmpty() ) continue;
7018     groupNames.insert    ( group->GetName() );
7019     groupDS->SetStoreName( group->GetName() );
7020     const SMDSAbs_ElementType type = groupDS->GetType();
7021     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7022     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7023     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7024     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7025   }
7026
7027   // Loop on nodes and elements to add them in new groups
7028
7029   vector< const SMDS_MeshElement* > resultElems;
7030   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7031   {
7032     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7033     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7034     if ( gens.Length() != elems.Length() )
7035       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7036
7037     // loop on created elements
7038     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7039     {
7040       const SMDS_MeshElement* sourceElem = gens( iElem );
7041       if ( !sourceElem ) {
7042         MESSAGE("generateGroups(): NULL source element");
7043         continue;
7044       }
7045       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7046       if ( groupsOldNew.empty() ) { // no groups of this type at all
7047         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7048           ++iElem; // skip all elements made by sourceElem
7049         continue;
7050       }
7051       // collect all elements made by the iElem-th sourceElem
7052       resultElems.clear();
7053       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7054         if ( resElem != sourceElem )
7055           resultElems.push_back( resElem );
7056       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7057         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7058           if ( resElem != sourceElem )
7059             resultElems.push_back( resElem );
7060
7061       const SMDS_MeshElement* topElem = 0;
7062       if ( isNodes ) // there must be a top element
7063       {
7064         topElem = resultElems.back();
7065         resultElems.pop_back();
7066       }
7067       else
7068       {
7069         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7070         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7071           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7072           {
7073             topElem = *resElemIt;
7074             *resElemIt = 0; // erase *resElemIt
7075             break;
7076           }
7077       }
7078       // add resultElems to groups originted from ones the sourceElem belongs to
7079       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7080       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7081       {
7082         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7083         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7084         {
7085           // fill in a new group
7086           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7087           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7088           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7089             if ( *resElemIt )
7090               newGroup.Add( *resElemIt );
7091
7092           // fill a "top" group
7093           if ( topElem )
7094           {
7095             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7096             newTopGroup.Add( topElem );
7097          }
7098         }
7099       }
7100     } // loop on created elements
7101   }// loop on nodes and elements
7102
7103   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7104
7105   list<int> topGrouIds;
7106   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7107   {
7108     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7109     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7110                                       orderedOldNewGroups[i]->get<2>() };
7111     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7112     {
7113       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7114       if ( newGroupDS->IsEmpty() )
7115       {
7116         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7117       }
7118       else
7119       {
7120         // set group type
7121         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7122
7123         // make a name
7124         const bool isTop = ( topPresent &&
7125                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7126                              is2nd );
7127
7128         string name = oldGroupDS->GetStoreName();
7129         { // remove trailing whitespaces (issue 22599)
7130           size_t size = name.size();
7131           while ( size > 1 && isspace( name[ size-1 ]))
7132             --size;
7133           if ( size != name.size() )
7134           {
7135             name.resize( size );
7136             oldGroupDS->SetStoreName( name.c_str() );
7137           }
7138         }
7139         if ( !targetMesh ) {
7140           string suffix = ( isTop ? "top": postfix.c_str() );
7141           name += "_";
7142           name += suffix;
7143           int nb = 1;
7144           while ( !groupNames.insert( name ).second ) // name exists
7145             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7146         }
7147         else if ( isTop ) {
7148           name += "_top";
7149         }
7150         newGroupDS->SetStoreName( name.c_str() );
7151
7152         // make a SMESH_Groups
7153         mesh->AddGroup( newGroupDS );
7154         if ( isTop )
7155           topGrouIds.push_back( newGroupDS->GetID() );
7156         else
7157           newGroupIDs->push_back( newGroupDS->GetID() );
7158       }
7159     }
7160   }
7161   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7162
7163   return newGroupIDs;
7164 }
7165
7166 //================================================================================
7167 /*!
7168  *  * \brief Return list of group of nodes close to each other within theTolerance
7169  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7170  *  *        an Octree algorithm
7171  *  \param [in,out] theNodes - the nodes to treat
7172  *  \param [in]     theTolerance - the tolerance
7173  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7174  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7175  *         corner and medium nodes in separate groups
7176  */
7177 //================================================================================
7178
7179 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7180                                             const double         theTolerance,
7181                                             TListOfListOfNodes & theGroupsOfNodes,
7182                                             bool                 theSeparateCornersAndMedium)
7183 {
7184   myLastCreatedElems.Clear();
7185   myLastCreatedNodes.Clear();
7186
7187   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7188        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7189        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7190     theSeparateCornersAndMedium = false;
7191
7192   TIDSortedNodeSet& corners = theNodes;
7193   TIDSortedNodeSet  medium;
7194
7195   if ( theNodes.empty() ) // get all nodes in the mesh
7196   {
7197     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7198     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7199     if ( theSeparateCornersAndMedium )
7200       while ( nIt->more() )
7201       {
7202         const SMDS_MeshNode* n = nIt->next();
7203         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7204         nodeSet->insert( nodeSet->end(), n );
7205       }
7206     else
7207       while ( nIt->more() )
7208         theNodes.insert( theNodes.end(),nIt->next() );
7209   }
7210   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7211   {
7212     TIDSortedNodeSet::iterator nIt = corners.begin();
7213     while ( nIt != corners.end() )
7214       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7215       {
7216         medium.insert( medium.end(), *nIt );
7217         corners.erase( nIt++ );
7218       }
7219       else
7220       {
7221         ++nIt;
7222       }
7223   }
7224
7225   if ( !corners.empty() )
7226     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7227   if ( !medium.empty() )
7228     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7229 }
7230
7231 //=======================================================================
7232 //function : SimplifyFace
7233 //purpose  : split a chain of nodes into several closed chains
7234 //=======================================================================
7235
7236 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7237                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7238                                     vector<int>&                         quantities) const
7239 {
7240   int nbNodes = faceNodes.size();
7241
7242   if (nbNodes < 3)
7243     return 0;
7244
7245   set<const SMDS_MeshNode*> nodeSet;
7246
7247   // get simple seq of nodes
7248   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7249   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7250   int iSimple = 0, nbUnique = 0;
7251
7252   simpleNodes[iSimple++] = faceNodes[0];
7253   nbUnique++;
7254   for (int iCur = 1; iCur < nbNodes; iCur++) {
7255     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7256       simpleNodes[iSimple++] = faceNodes[iCur];
7257       if (nodeSet.insert( faceNodes[iCur] ).second)
7258         nbUnique++;
7259     }
7260   }
7261   int nbSimple = iSimple;
7262   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7263     nbSimple--;
7264     iSimple--;
7265   }
7266
7267   if (nbUnique < 3)
7268     return 0;
7269
7270   // separate loops
7271   int nbNew = 0;
7272   bool foundLoop = (nbSimple > nbUnique);
7273   while (foundLoop) {
7274     foundLoop = false;
7275     set<const SMDS_MeshNode*> loopSet;
7276     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7277       const SMDS_MeshNode* n = simpleNodes[iSimple];
7278       if (!loopSet.insert( n ).second) {
7279         foundLoop = true;
7280
7281         // separate loop
7282         int iC = 0, curLast = iSimple;
7283         for (; iC < curLast; iC++) {
7284           if (simpleNodes[iC] == n) break;
7285         }
7286         int loopLen = curLast - iC;
7287         if (loopLen > 2) {
7288           // create sub-element
7289           nbNew++;
7290           quantities.push_back(loopLen);
7291           for (; iC < curLast; iC++) {
7292             poly_nodes.push_back(simpleNodes[iC]);
7293           }
7294         }
7295         // shift the rest nodes (place from the first loop position)
7296         for (iC = curLast + 1; iC < nbSimple; iC++) {
7297           simpleNodes[iC - loopLen] = simpleNodes[iC];
7298         }
7299         nbSimple -= loopLen;
7300         iSimple -= loopLen;
7301       }
7302     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7303   } // while (foundLoop)
7304
7305   if (iSimple > 2) {
7306     nbNew++;
7307     quantities.push_back(iSimple);
7308     for (int i = 0; i < iSimple; i++)
7309       poly_nodes.push_back(simpleNodes[i]);
7310   }
7311
7312   return nbNew;
7313 }
7314
7315 //=======================================================================
7316 //function : MergeNodes
7317 //purpose  : In each group, the cdr of nodes are substituted by the first one
7318 //           in all elements.
7319 //=======================================================================
7320
7321 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7322 {
7323   MESSAGE("MergeNodes");
7324   myLastCreatedElems.Clear();
7325   myLastCreatedNodes.Clear();
7326
7327   SMESHDS_Mesh* aMesh = GetMeshDS();
7328
7329   TNodeNodeMap nodeNodeMap; // node to replace - new node
7330   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7331   list< int > rmElemIds, rmNodeIds;
7332
7333   // Fill nodeNodeMap and elems
7334
7335   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7336   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7337   {
7338     list<const SMDS_MeshNode*>& nodes = *grIt;
7339     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7340     const SMDS_MeshNode* nToKeep = *nIt;
7341     for ( ++nIt; nIt != nodes.end(); nIt++ )
7342     {
7343       const SMDS_MeshNode* nToRemove = *nIt;
7344       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7345       if ( nToRemove != nToKeep )
7346       {
7347         rmNodeIds.push_back( nToRemove->GetID() );
7348         AddToSameGroups( nToKeep, nToRemove, aMesh );
7349         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7350         // after MergeNodes() w/o creating node in place of merged ones.
7351         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7352         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7353           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7354             sm->SetIsAlwaysComputed( true );
7355       }
7356       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7357       while ( invElemIt->more() ) {
7358         const SMDS_MeshElement* elem = invElemIt->next();
7359         elems.insert(elem);
7360       }
7361     }
7362   }
7363   // Change element nodes or remove an element
7364
7365   set<const SMDS_MeshNode*> nodeSet;
7366   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7367   vector<int> iRepl;
7368   ElemFeatures elemType;
7369
7370   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7371   for ( ; eIt != elems.end(); eIt++ )
7372   {
7373     const SMDS_MeshElement* elem = *eIt;
7374     int nbNodes = elem->NbNodes();
7375     int aShapeId = FindShape( elem );
7376
7377     nodeSet.clear();
7378     curNodes.resize( nbNodes );
7379     uniqueNodes.resize( nbNodes );
7380     iRepl.resize( nbNodes );
7381     int iUnique = 0, iCur = 0, nbRepl = 0;
7382
7383     // get new seq of nodes
7384     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7385     while ( itN->more() )
7386     {
7387       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7388
7389       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7390       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7391         n = (*nnIt).second;
7392         { ////////// BUG 0020185: begin
7393           bool stopRecur = false;
7394           set<const SMDS_MeshNode*> nodesRecur;
7395           nodesRecur.insert(n);
7396           while (!stopRecur) {
7397             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7398             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7399               n = (*nnIt_i).second;
7400               if (!nodesRecur.insert(n).second) {
7401                 // error: recursive dependancy
7402                 stopRecur = true;
7403               }
7404             }
7405             else
7406               stopRecur = true;
7407           }
7408         } ////////// BUG 0020185: end
7409       }
7410       curNodes[ iCur ] = n;
7411       bool isUnique = nodeSet.insert( n ).second;
7412       if ( isUnique )
7413         uniqueNodes[ iUnique++ ] = n;
7414       else
7415         iRepl[ nbRepl++ ] = iCur;
7416       iCur++;
7417     }
7418
7419     // Analyse element topology after replacement
7420
7421     bool isOk = true;
7422     int nbUniqueNodes = nodeSet.size();
7423     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7424     {
7425       if (elem->IsPoly()) // Polygons and Polyhedral volumes
7426       {
7427         if (elem->GetType() == SMDSAbs_Face) // Polygon
7428         {
7429           elemType.Init( elem );
7430           const bool isQuad = elemType.myIsQuad;
7431           if ( isQuad )
7432             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7433               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7434
7435           // a polygon can divide into several elements
7436           vector<const SMDS_MeshNode *> polygons_nodes;
7437           vector<int> quantities;
7438           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7439           if (nbNew > 0)
7440           {
7441             vector<const SMDS_MeshNode *> face_nodes;
7442             int inode = 0;
7443             for (int iface = 0; iface < nbNew; iface++)
7444             {
7445               int nbNewNodes = quantities[iface];
7446               face_nodes.assign( polygons_nodes.begin() + inode,
7447                                  polygons_nodes.begin() + inode + nbNewNodes );
7448               inode += nbNewNodes;
7449               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7450               {
7451                 bool isValid = ( nbNewNodes % 2 == 0 );
7452                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7453                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7454                 elemType.SetQuad( isValid );
7455                 if ( isValid ) // put medium nodes after corners
7456                   SMDS_MeshCell::applyInterlaceRev
7457                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7458                                                           nbNewNodes ), face_nodes );
7459               }
7460               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7461               if ( aShapeId )
7462                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7463             }
7464           }
7465           rmElemIds.push_back(elem->GetID());
7466
7467         } // Polygon
7468
7469         else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7470         {
7471           if (nbUniqueNodes < 4) {
7472             rmElemIds.push_back(elem->GetID());
7473           }
7474           else {
7475             // each face has to be analyzed in order to check volume validity
7476             const SMDS_VtkVolume* aPolyedre =
7477               dynamic_cast<const SMDS_VtkVolume*>( elem );
7478             if (aPolyedre) {
7479               int nbFaces = aPolyedre->NbFaces();
7480
7481               vector<const SMDS_MeshNode *> poly_nodes;
7482               vector<int> quantities;
7483
7484               for (int iface = 1; iface <= nbFaces; iface++) {
7485                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7486                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7487
7488                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7489                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7490                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7491                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7492                     faceNode = (*nnIt).second;
7493                   }
7494                   faceNodes[inode - 1] = faceNode;
7495                 }
7496
7497                 SimplifyFace(faceNodes, poly_nodes, quantities);
7498               }
7499
7500               if (quantities.size() > 3) {
7501                 // to be done: remove coincident faces
7502               }
7503
7504               if (quantities.size() > 3)
7505               {
7506                 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7507                 const SMDS_MeshElement* newElem =
7508                   aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7509                 myLastCreatedElems.Append(newElem);
7510                 if ( aShapeId && newElem )
7511                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7512                 rmElemIds.push_back(elem->GetID());
7513               }
7514             }
7515             else {
7516               rmElemIds.push_back(elem->GetID());
7517             }
7518           }
7519         }
7520         else {
7521         }
7522
7523         continue;
7524       } // poly element
7525
7526       // Regular elements
7527       // TODO not all the possible cases are solved. Find something more generic?
7528       switch ( nbNodes ) {
7529       case 2: ///////////////////////////////////// EDGE
7530         isOk = false; break;
7531       case 3: ///////////////////////////////////// TRIANGLE
7532         isOk = false; break;
7533       case 4:
7534         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7535           isOk = false;
7536         else { //////////////////////////////////// QUADRANGLE
7537           if ( nbUniqueNodes < 3 )
7538             isOk = false;
7539           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7540             isOk = false; // opposite nodes stick
7541           //MESSAGE("isOk " << isOk);
7542         }
7543         break;
7544       case 6: ///////////////////////////////////// PENTAHEDRON
7545         if ( nbUniqueNodes == 4 ) {
7546           // ---------------------------------> tetrahedron
7547           if (nbRepl == 3 &&
7548               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7549             // all top nodes stick: reverse a bottom
7550             uniqueNodes[ 0 ] = curNodes [ 1 ];
7551             uniqueNodes[ 1 ] = curNodes [ 0 ];
7552           }
7553           else if (nbRepl == 3 &&
7554                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7555             // all bottom nodes stick: set a top before
7556             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7557             uniqueNodes[ 0 ] = curNodes [ 3 ];
7558             uniqueNodes[ 1 ] = curNodes [ 4 ];
7559             uniqueNodes[ 2 ] = curNodes [ 5 ];
7560           }
7561           else if (nbRepl == 4 &&
7562                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7563             // a lateral face turns into a line: reverse a bottom
7564             uniqueNodes[ 0 ] = curNodes [ 1 ];
7565             uniqueNodes[ 1 ] = curNodes [ 0 ];
7566           }
7567           else
7568             isOk = false;
7569         }
7570         else if ( nbUniqueNodes == 5 ) {
7571           // PENTAHEDRON --------------------> 2 tetrahedrons
7572           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7573             // a bottom node sticks with a linked top one
7574             // 1.
7575             SMDS_MeshElement* newElem =
7576               aMesh->AddVolume(curNodes[ 3 ],
7577                                curNodes[ 4 ],
7578                                curNodes[ 5 ],
7579                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7580             myLastCreatedElems.Append(newElem);
7581             if ( aShapeId )
7582               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7583             // 2. : reverse a bottom
7584             uniqueNodes[ 0 ] = curNodes [ 1 ];
7585             uniqueNodes[ 1 ] = curNodes [ 0 ];
7586             nbUniqueNodes = 4;
7587           }
7588           else
7589             isOk = false;
7590         }
7591         else
7592           isOk = false;
7593         break;
7594       case 8: {
7595         if(elem->IsQuadratic()) { // Quadratic quadrangle
7596           //   1    5    2
7597           //    +---+---+
7598           //    |       |
7599           //    |       |
7600           //   4+       +6
7601           //    |       |
7602           //    |       |
7603           //    +---+---+
7604           //   0    7    3
7605           isOk = false;
7606           if(nbRepl==2) {
7607             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7608           }
7609           if(nbRepl==3) {
7610             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7611             nbUniqueNodes = 6;
7612             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7613               uniqueNodes[0] = curNodes[0];
7614               uniqueNodes[1] = curNodes[2];
7615               uniqueNodes[2] = curNodes[3];
7616               uniqueNodes[3] = curNodes[5];
7617               uniqueNodes[4] = curNodes[6];
7618               uniqueNodes[5] = curNodes[7];
7619               isOk = true;
7620             }
7621             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7622               uniqueNodes[0] = curNodes[0];
7623               uniqueNodes[1] = curNodes[1];
7624               uniqueNodes[2] = curNodes[2];
7625               uniqueNodes[3] = curNodes[4];
7626               uniqueNodes[4] = curNodes[5];
7627               uniqueNodes[5] = curNodes[6];
7628               isOk = true;
7629             }
7630             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7631               uniqueNodes[0] = curNodes[1];
7632               uniqueNodes[1] = curNodes[2];
7633               uniqueNodes[2] = curNodes[3];
7634               uniqueNodes[3] = curNodes[5];
7635               uniqueNodes[4] = curNodes[6];
7636               uniqueNodes[5] = curNodes[0];
7637               isOk = true;
7638             }
7639             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7640               uniqueNodes[0] = curNodes[0];
7641               uniqueNodes[1] = curNodes[1];
7642               uniqueNodes[2] = curNodes[3];
7643               uniqueNodes[3] = curNodes[4];
7644               uniqueNodes[4] = curNodes[6];
7645               uniqueNodes[5] = curNodes[7];
7646               isOk = true;
7647             }
7648             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7649               uniqueNodes[0] = curNodes[0];
7650               uniqueNodes[1] = curNodes[2];
7651               uniqueNodes[2] = curNodes[3];
7652               uniqueNodes[3] = curNodes[1];
7653               uniqueNodes[4] = curNodes[6];
7654               uniqueNodes[5] = curNodes[7];
7655               isOk = true;
7656             }
7657             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7658               uniqueNodes[0] = curNodes[0];
7659               uniqueNodes[1] = curNodes[1];
7660               uniqueNodes[2] = curNodes[2];
7661               uniqueNodes[3] = curNodes[4];
7662               uniqueNodes[4] = curNodes[5];
7663               uniqueNodes[5] = curNodes[7];
7664               isOk = true;
7665             }
7666             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7667               uniqueNodes[0] = curNodes[0];
7668               uniqueNodes[1] = curNodes[1];
7669               uniqueNodes[2] = curNodes[3];
7670               uniqueNodes[3] = curNodes[4];
7671               uniqueNodes[4] = curNodes[2];
7672               uniqueNodes[5] = curNodes[7];
7673               isOk = true;
7674             }
7675             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7676               uniqueNodes[0] = curNodes[0];
7677               uniqueNodes[1] = curNodes[1];
7678               uniqueNodes[2] = curNodes[2];
7679               uniqueNodes[3] = curNodes[4];
7680               uniqueNodes[4] = curNodes[5];
7681               uniqueNodes[5] = curNodes[3];
7682               isOk = true;
7683             }
7684           }
7685           if(nbRepl==4) {
7686             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7687           }
7688           if(nbRepl==5) {
7689             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7690           }
7691           break;
7692         }
7693         //////////////////////////////////// HEXAHEDRON
7694         isOk = false;
7695         SMDS_VolumeTool hexa (elem);
7696         hexa.SetExternalNormal();
7697         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7698           //////////////////////// HEX ---> 1 tetrahedron
7699           for ( int iFace = 0; iFace < 6; iFace++ ) {
7700             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7701             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7702                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7703                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7704               // one face turns into a point ...
7705               int iOppFace = hexa.GetOppFaceIndex( iFace );
7706               ind = hexa.GetFaceNodesIndices( iOppFace );
7707               int nbStick = 0;
7708               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7709                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7710                   nbStick++;
7711               }
7712               if ( nbStick == 1 ) {
7713                 // ... and the opposite one - into a triangle.
7714                 // set a top node
7715                 ind = hexa.GetFaceNodesIndices( iFace );
7716                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7717                 isOk = true;
7718               }
7719               break;
7720             }
7721           }
7722         }
7723         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7724           //////////////////////// HEX ---> 1 prism
7725           int nbTria = 0, iTria[3];
7726           const int *ind; // indices of face nodes
7727           // look for triangular faces
7728           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7729             ind = hexa.GetFaceNodesIndices( iFace );
7730             TIDSortedNodeSet faceNodes;
7731             for ( iCur = 0; iCur < 4; iCur++ )
7732               faceNodes.insert( curNodes[ind[iCur]] );
7733             if ( faceNodes.size() == 3 )
7734               iTria[ nbTria++ ] = iFace;
7735           }
7736           // check if triangles are opposite
7737           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7738           {
7739             isOk = true;
7740             // set nodes of the bottom triangle
7741             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7742             vector<int> indB;
7743             for ( iCur = 0; iCur < 4; iCur++ )
7744               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7745                 indB.push_back( ind[iCur] );
7746             if ( !hexa.IsForward() )
7747               std::swap( indB[0], indB[2] );
7748             for ( iCur = 0; iCur < 3; iCur++ )
7749               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7750             // set nodes of the top triangle
7751             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7752             for ( iCur = 0; iCur < 3; ++iCur )
7753               for ( int j = 0; j < 4; ++j )
7754                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7755                 {
7756                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7757                   break;
7758                 }
7759           }
7760           break;
7761         }
7762         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7763           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7764           for ( int iFace = 0; iFace < 6; iFace++ ) {
7765             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7766             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7767                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7768                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7769               // one face turns into a point ...
7770               int iOppFace = hexa.GetOppFaceIndex( iFace );
7771               ind = hexa.GetFaceNodesIndices( iOppFace );
7772               int nbStick = 0;
7773               iUnique = 2;  // reverse a tetrahedron 1 bottom
7774               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7775                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7776                   nbStick++;
7777                 else if ( iUnique >= 0 )
7778                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7779               }
7780               if ( nbStick == 0 ) {
7781                 // ... and the opposite one is a quadrangle
7782                 // set a top node
7783                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7784                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7785                 nbUniqueNodes = 4;
7786                 // tetrahedron 2
7787                 SMDS_MeshElement* newElem =
7788                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7789                                    curNodes[ind[ 3 ]],
7790                                    curNodes[ind[ 2 ]],
7791                                    curNodes[indTop[ 0 ]]);
7792                 myLastCreatedElems.Append(newElem);
7793                 if ( aShapeId )
7794                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7795                 isOk = true;
7796               }
7797               break;
7798             }
7799           }
7800         }
7801         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7802           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7803           // find indices of quad and tri faces
7804           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7805           for ( iFace = 0; iFace < 6; iFace++ ) {
7806             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7807             nodeSet.clear();
7808             for ( iCur = 0; iCur < 4; iCur++ )
7809               nodeSet.insert( curNodes[ind[ iCur ]] );
7810             nbUniqueNodes = nodeSet.size();
7811             if ( nbUniqueNodes == 3 )
7812               iTriFace[ nbTri++ ] = iFace;
7813             else if ( nbUniqueNodes == 4 )
7814               iQuadFace[ nbQuad++ ] = iFace;
7815           }
7816           if (nbQuad == 2 && nbTri == 4 &&
7817               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7818             // 2 opposite quadrangles stuck with a diagonal;
7819             // sample groups of merged indices: (0-4)(2-6)
7820             // --------------------------------------------> 2 tetrahedrons
7821             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7822             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7823             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7824             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7825                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7826               // stuck with 0-2 diagonal
7827               i0  = ind1[ 3 ];
7828               i1d = ind1[ 0 ];
7829               i2  = ind1[ 1 ];
7830               i3d = ind1[ 2 ];
7831               i0t = ind2[ 1 ];
7832               i2t = ind2[ 3 ];
7833             }
7834             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7835                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7836               // stuck with 1-3 diagonal
7837               i0  = ind1[ 0 ];
7838               i1d = ind1[ 1 ];
7839               i2  = ind1[ 2 ];
7840               i3d = ind1[ 3 ];
7841               i0t = ind2[ 0 ];
7842               i2t = ind2[ 1 ];
7843             }
7844             else {
7845               ASSERT(0);
7846             }
7847             // tetrahedron 1
7848             uniqueNodes[ 0 ] = curNodes [ i0 ];
7849             uniqueNodes[ 1 ] = curNodes [ i1d ];
7850             uniqueNodes[ 2 ] = curNodes [ i3d ];
7851             uniqueNodes[ 3 ] = curNodes [ i0t ];
7852             nbUniqueNodes = 4;
7853             // tetrahedron 2
7854             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7855                                                          curNodes[ i2 ],
7856                                                          curNodes[ i3d ],
7857                                                          curNodes[ i2t ]);
7858             myLastCreatedElems.Append(newElem);
7859             if ( aShapeId )
7860               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7861             isOk = true;
7862           }
7863           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7864                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7865             // --------------------------------------------> prism
7866             // find 2 opposite triangles
7867             nbUniqueNodes = 6;
7868             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7869               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7870                 // find indices of kept and replaced nodes
7871                 // and fill unique nodes of 2 opposite triangles
7872                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7873                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7874                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7875                 // fill unique nodes
7876                 iUnique = 0;
7877                 isOk = true;
7878                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7879                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7880                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7881                   if ( n == nInit ) {
7882                     // iCur of a linked node of the opposite face (make normals co-directed):
7883                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7884                     // check that correspondent corners of triangles are linked
7885                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7886                       isOk = false;
7887                     else {
7888                       uniqueNodes[ iUnique ] = n;
7889                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7890                       iUnique++;
7891                     }
7892                   }
7893                 }
7894                 break;
7895               }
7896             }
7897           }
7898         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7899         else
7900         {
7901           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7902         }
7903         break;
7904       } // HEXAHEDRON
7905
7906       default:
7907         isOk = false;
7908       } // switch ( nbNodes )
7909
7910     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7911
7912     if ( isOk ) // the non-poly elem remains valid after sticking nodes
7913     {
7914       elemType.Init( elem ).SetID( elem->GetID() );
7915
7916       SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7917       aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7918
7919       uniqueNodes.resize(nbUniqueNodes);
7920       SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7921       if ( sm && newElem )
7922         sm->AddElement( newElem );
7923       if ( elem != newElem )
7924         ReplaceElemInGroups( elem, newElem, aMesh );
7925     }
7926     else {
7927       // Remove invalid regular element or invalid polygon
7928       rmElemIds.push_back( elem->GetID() );
7929     }
7930
7931   } // loop on elements
7932
7933   // Remove bad elements, then equal nodes (order important)
7934
7935   Remove( rmElemIds, false );
7936   Remove( rmNodeIds, true );
7937
7938   return;
7939 }
7940
7941
7942 // ========================================================
7943 // class   : SortableElement
7944 // purpose : allow sorting elements basing on their nodes
7945 // ========================================================
7946 class SortableElement : public set <const SMDS_MeshElement*>
7947 {
7948 public:
7949
7950   SortableElement( const SMDS_MeshElement* theElem )
7951   {
7952     myElem = theElem;
7953     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7954     while ( nodeIt->more() )
7955       this->insert( nodeIt->next() );
7956   }
7957
7958   const SMDS_MeshElement* Get() const
7959   { return myElem; }
7960
7961   void Set(const SMDS_MeshElement* e) const
7962   { myElem = e; }
7963
7964
7965 private:
7966   mutable const SMDS_MeshElement* myElem;
7967 };
7968
7969 //=======================================================================
7970 //function : FindEqualElements
7971 //purpose  : Return list of group of elements built on the same nodes.
7972 //           Search among theElements or in the whole mesh if theElements is empty
7973 //=======================================================================
7974
7975 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7976                                          TListOfListOfElementsID & theGroupsOfElementsID)
7977 {
7978   myLastCreatedElems.Clear();
7979   myLastCreatedNodes.Clear();
7980
7981   typedef map< SortableElement, int > TMapOfNodeSet;
7982   typedef list<int> TGroupOfElems;
7983
7984   if ( theElements.empty() )
7985   { // get all elements in the mesh
7986     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7987     while ( eIt->more() )
7988       theElements.insert( theElements.end(), eIt->next() );
7989   }
7990
7991   vector< TGroupOfElems > arrayOfGroups;
7992   TGroupOfElems groupOfElems;
7993   TMapOfNodeSet mapOfNodeSet;
7994
7995   TIDSortedElemSet::iterator elemIt = theElements.begin();
7996   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7997   {
7998     const SMDS_MeshElement* curElem = *elemIt;
7999     SortableElement SE(curElem);
8000     // check uniqueness
8001     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8002     if ( !pp.second ) { // one more coincident elem
8003       TMapOfNodeSet::iterator& itSE = pp.first;
8004       int ind = (*itSE).second;
8005       arrayOfGroups[ind].push_back( curElem->GetID() );
8006     }
8007     else {
8008       arrayOfGroups.push_back( groupOfElems );
8009       arrayOfGroups.back().push_back( curElem->GetID() );
8010       i++;
8011     }
8012   }
8013
8014   groupOfElems.clear();
8015   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8016   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8017   {
8018     if ( groupIt->size() > 1 ) {
8019       //groupOfElems.sort(); -- theElements is sorted already
8020       theGroupsOfElementsID.push_back( groupOfElems );
8021       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8022     }
8023   }
8024 }
8025
8026 //=======================================================================
8027 //function : MergeElements
8028 //purpose  : In each given group, substitute all elements by the first one.
8029 //=======================================================================
8030
8031 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8032 {
8033   myLastCreatedElems.Clear();
8034   myLastCreatedNodes.Clear();
8035
8036   typedef list<int> TListOfIDs;
8037   TListOfIDs rmElemIds; // IDs of elems to remove
8038
8039   SMESHDS_Mesh* aMesh = GetMeshDS();
8040
8041   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8042   while ( groupsIt != theGroupsOfElementsID.end() ) {
8043     TListOfIDs& aGroupOfElemID = *groupsIt;
8044     aGroupOfElemID.sort();
8045     int elemIDToKeep = aGroupOfElemID.front();
8046     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8047     aGroupOfElemID.pop_front();
8048     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8049     while ( idIt != aGroupOfElemID.end() ) {
8050       int elemIDToRemove = *idIt;
8051       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8052       // add the kept element in groups of removed one (PAL15188)
8053       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8054       rmElemIds.push_back( elemIDToRemove );
8055       ++idIt;
8056     }
8057     ++groupsIt;
8058   }
8059
8060   Remove( rmElemIds, false );
8061 }
8062
8063 //=======================================================================
8064 //function : MergeEqualElements
8065 //purpose  : Remove all but one of elements built on the same nodes.
8066 //=======================================================================
8067
8068 void SMESH_MeshEditor::MergeEqualElements()
8069 {
8070   TIDSortedElemSet aMeshElements; /* empty input ==
8071                                      to merge equal elements in the whole mesh */
8072   TListOfListOfElementsID aGroupsOfElementsID;
8073   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8074   MergeElements(aGroupsOfElementsID);
8075 }
8076
8077 //=======================================================================
8078 //function : findAdjacentFace
8079 //purpose  :
8080 //=======================================================================
8081
8082 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8083                                                 const SMDS_MeshNode* n2,
8084                                                 const SMDS_MeshElement* elem)
8085 {
8086   TIDSortedElemSet elemSet, avoidSet;
8087   if ( elem )
8088     avoidSet.insert ( elem );
8089   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 }
8091
8092 //=======================================================================
8093 //function : FindFreeBorder
8094 //purpose  :
8095 //=======================================================================
8096
8097 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8098
8099 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8100                                        const SMDS_MeshNode*             theSecondNode,
8101                                        const SMDS_MeshNode*             theLastNode,
8102                                        list< const SMDS_MeshNode* > &   theNodes,
8103                                        list< const SMDS_MeshElement* >& theFaces)
8104 {
8105   if ( !theFirstNode || !theSecondNode )
8106     return false;
8107   // find border face between theFirstNode and theSecondNode
8108   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8109   if ( !curElem )
8110     return false;
8111
8112   theFaces.push_back( curElem );
8113   theNodes.push_back( theFirstNode );
8114   theNodes.push_back( theSecondNode );
8115
8116   //vector<const SMDS_MeshNode*> nodes;
8117   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8118   TIDSortedElemSet foundElems;
8119   bool needTheLast = ( theLastNode != 0 );
8120
8121   while ( nStart != theLastNode ) {
8122     if ( nStart == theFirstNode )
8123       return !needTheLast;
8124
8125     // find all free border faces sharing form nStart
8126
8127     list< const SMDS_MeshElement* > curElemList;
8128     list< const SMDS_MeshNode* > nStartList;
8129     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8130     while ( invElemIt->more() ) {
8131       const SMDS_MeshElement* e = invElemIt->next();
8132       if ( e == curElem || foundElems.insert( e ).second ) {
8133         // get nodes
8134         int iNode = 0, nbNodes = e->NbNodes();
8135         //const SMDS_MeshNode* nodes[nbNodes+1];
8136         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8137
8138         if(e->IsQuadratic()) {
8139           const SMDS_VtkFace* F =
8140             dynamic_cast<const SMDS_VtkFace*>(e);
8141           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8142           // use special nodes iterator
8143           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8144           while( anIter->more() ) {
8145             nodes[ iNode++ ] = cast2Node(anIter->next());
8146           }
8147         }
8148         else {
8149           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8150           while ( nIt->more() )
8151             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8152         }
8153         nodes[ iNode ] = nodes[ 0 ];
8154         // check 2 links
8155         for ( iNode = 0; iNode < nbNodes; iNode++ )
8156           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8157                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8158               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8159           {
8160             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8161             curElemList.push_back( e );
8162           }
8163       }
8164     }
8165     // analyse the found
8166
8167     int nbNewBorders = curElemList.size();
8168     if ( nbNewBorders == 0 ) {
8169       // no free border furthermore
8170       return !needTheLast;
8171     }
8172     else if ( nbNewBorders == 1 ) {
8173       // one more element found
8174       nIgnore = nStart;
8175       nStart = nStartList.front();
8176       curElem = curElemList.front();
8177       theFaces.push_back( curElem );
8178       theNodes.push_back( nStart );
8179     }
8180     else {
8181       // several continuations found
8182       list< const SMDS_MeshElement* >::iterator curElemIt;
8183       list< const SMDS_MeshNode* >::iterator nStartIt;
8184       // check if one of them reached the last node
8185       if ( needTheLast ) {
8186         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8187              curElemIt!= curElemList.end();
8188              curElemIt++, nStartIt++ )
8189           if ( *nStartIt == theLastNode ) {
8190             theFaces.push_back( *curElemIt );
8191             theNodes.push_back( *nStartIt );
8192             return true;
8193           }
8194       }
8195       // find the best free border by the continuations
8196       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8197       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8198       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8199            curElemIt!= curElemList.end();
8200            curElemIt++, nStartIt++ )
8201       {
8202         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8203         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8204         // find one more free border
8205         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8206           cNL->clear();
8207           cFL->clear();
8208         }
8209         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8210           // choice: clear a worse one
8211           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8212           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8213           contNodes[ iWorse ].clear();
8214           contFaces[ iWorse ].clear();
8215         }
8216       }
8217       if ( contNodes[0].empty() && contNodes[1].empty() )
8218         return false;
8219
8220       // append the best free border
8221       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8222       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8223       theNodes.pop_back(); // remove nIgnore
8224       theNodes.pop_back(); // remove nStart
8225       theFaces.pop_back(); // remove curElem
8226       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8227       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8228       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8229       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8230       return true;
8231
8232     } // several continuations found
8233   } // while ( nStart != theLastNode )
8234
8235   return true;
8236 }
8237
8238 //=======================================================================
8239 //function : CheckFreeBorderNodes
8240 //purpose  : Return true if the tree nodes are on a free border
8241 //=======================================================================
8242
8243 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8244                                             const SMDS_MeshNode* theNode2,
8245                                             const SMDS_MeshNode* theNode3)
8246 {
8247   list< const SMDS_MeshNode* > nodes;
8248   list< const SMDS_MeshElement* > faces;
8249   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8250 }
8251
8252 //=======================================================================
8253 //function : SewFreeBorder
8254 //purpose  :
8255 //=======================================================================
8256
8257 SMESH_MeshEditor::Sew_Error
8258 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8259                                  const SMDS_MeshNode* theBordSecondNode,
8260                                  const SMDS_MeshNode* theBordLastNode,
8261                                  const SMDS_MeshNode* theSideFirstNode,
8262                                  const SMDS_MeshNode* theSideSecondNode,
8263                                  const SMDS_MeshNode* theSideThirdNode,
8264                                  const bool           theSideIsFreeBorder,
8265                                  const bool           toCreatePolygons,
8266                                  const bool           toCreatePolyedrs)
8267 {
8268   myLastCreatedElems.Clear();
8269   myLastCreatedNodes.Clear();
8270
8271   MESSAGE("::SewFreeBorder()");
8272   Sew_Error aResult = SEW_OK;
8273
8274   // ====================================
8275   //    find side nodes and elements
8276   // ====================================
8277
8278   list< const SMDS_MeshNode* > nSide[ 2 ];
8279   list< const SMDS_MeshElement* > eSide[ 2 ];
8280   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8281   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8282
8283   // Free border 1
8284   // --------------
8285   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8286                       nSide[0], eSide[0])) {
8287     MESSAGE(" Free Border 1 not found " );
8288     aResult = SEW_BORDER1_NOT_FOUND;
8289   }
8290   if (theSideIsFreeBorder) {
8291     // Free border 2
8292     // --------------
8293     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8294                         nSide[1], eSide[1])) {
8295       MESSAGE(" Free Border 2 not found " );
8296       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8297     }
8298   }
8299   if ( aResult != SEW_OK )
8300     return aResult;
8301
8302   if (!theSideIsFreeBorder) {
8303     // Side 2
8304     // --------------
8305
8306     // -------------------------------------------------------------------------
8307     // Algo:
8308     // 1. If nodes to merge are not coincident, move nodes of the free border
8309     //    from the coord sys defined by the direction from the first to last
8310     //    nodes of the border to the correspondent sys of the side 2
8311     // 2. On the side 2, find the links most co-directed with the correspondent
8312     //    links of the free border
8313     // -------------------------------------------------------------------------
8314
8315     // 1. Since sewing may break if there are volumes to split on the side 2,
8316     //    we wont move nodes but just compute new coordinates for them
8317     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8318     TNodeXYZMap nBordXYZ;
8319     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8320     list< const SMDS_MeshNode* >::iterator nBordIt;
8321
8322     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8323     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8324     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8325     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8326     double tol2 = 1.e-8;
8327     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8328     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8329       // Need node movement.
8330
8331       // find X and Z axes to create trsf
8332       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8333       gp_Vec X = Zs ^ Zb;
8334       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8335         // Zb || Zs
8336         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8337
8338       // coord systems
8339       gp_Ax3 toBordAx( Pb1, Zb, X );
8340       gp_Ax3 fromSideAx( Ps1, Zs, X );
8341       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8342       // set trsf
8343       gp_Trsf toBordSys, fromSide2Sys;
8344       toBordSys.SetTransformation( toBordAx );
8345       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8346       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8347
8348       // move
8349       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8350         const SMDS_MeshNode* n = *nBordIt;
8351         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8352         toBordSys.Transforms( xyz );
8353         fromSide2Sys.Transforms( xyz );
8354         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8355       }
8356     }
8357     else {
8358       // just insert nodes XYZ in the nBordXYZ map
8359       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8360         const SMDS_MeshNode* n = *nBordIt;
8361         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8362       }
8363     }
8364
8365     // 2. On the side 2, find the links most co-directed with the correspondent
8366     //    links of the free border
8367
8368     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8369     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8370     sideNodes.push_back( theSideFirstNode );
8371
8372     bool hasVolumes = false;
8373     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8374     set<long> foundSideLinkIDs, checkedLinkIDs;
8375     SMDS_VolumeTool volume;
8376     //const SMDS_MeshNode* faceNodes[ 4 ];
8377
8378     const SMDS_MeshNode*    sideNode;
8379     const SMDS_MeshElement* sideElem;
8380     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8381     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8382     nBordIt = bordNodes.begin();
8383     nBordIt++;
8384     // border node position and border link direction to compare with
8385     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8386     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8387     // choose next side node by link direction or by closeness to
8388     // the current border node:
8389     bool searchByDir = ( *nBordIt != theBordLastNode );
8390     do {
8391       // find the next node on the Side 2
8392       sideNode = 0;
8393       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8394       long linkID;
8395       checkedLinkIDs.clear();
8396       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8397
8398       // loop on inverse elements of current node (prevSideNode) on the Side 2
8399       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8400       while ( invElemIt->more() )
8401       {
8402         const SMDS_MeshElement* elem = invElemIt->next();
8403         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8404         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8405         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8406         bool isVolume = volume.Set( elem );
8407         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8408         if ( isVolume ) // --volume
8409           hasVolumes = true;
8410         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8411           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8412           if(elem->IsQuadratic()) {
8413             const SMDS_VtkFace* F =
8414               dynamic_cast<const SMDS_VtkFace*>(elem);
8415             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8416             // use special nodes iterator
8417             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8418             while( anIter->more() ) {
8419               nodes[ iNode ] = cast2Node(anIter->next());
8420               if ( nodes[ iNode++ ] == prevSideNode )
8421                 iPrevNode = iNode - 1;
8422             }
8423           }
8424           else {
8425             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8426             while ( nIt->more() ) {
8427               nodes[ iNode ] = cast2Node( nIt->next() );
8428               if ( nodes[ iNode++ ] == prevSideNode )
8429                 iPrevNode = iNode - 1;
8430             }
8431           }
8432           // there are 2 links to check
8433           nbNodes = 2;
8434         }
8435         else // --edge
8436           continue;
8437         // loop on links, to be precise, on the second node of links
8438         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8439           const SMDS_MeshNode* n = nodes[ iNode ];
8440           if ( isVolume ) {
8441             if ( !volume.IsLinked( n, prevSideNode ))
8442               continue;
8443           }
8444           else {
8445             if ( iNode ) // a node before prevSideNode
8446               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8447             else         // a node after prevSideNode
8448               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8449           }
8450           // check if this link was already used
8451           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8452           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8453           if (!isJustChecked &&
8454               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8455           {
8456             // test a link geometrically
8457             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8458             bool linkIsBetter = false;
8459             double dot = 0.0, dist = 0.0;
8460             if ( searchByDir ) { // choose most co-directed link
8461               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8462               linkIsBetter = ( dot > maxDot );
8463             }
8464             else { // choose link with the node closest to bordPos
8465               dist = ( nextXYZ - bordPos ).SquareModulus();
8466               linkIsBetter = ( dist < minDist );
8467             }
8468             if ( linkIsBetter ) {
8469               maxDot = dot;
8470               minDist = dist;
8471               linkID = iLink;
8472               sideNode = n;
8473               sideElem = elem;
8474             }
8475           }
8476         }
8477       } // loop on inverse elements of prevSideNode
8478
8479       if ( !sideNode ) {
8480         MESSAGE(" Cant find path by links of the Side 2 ");
8481         return SEW_BAD_SIDE_NODES;
8482       }
8483       sideNodes.push_back( sideNode );
8484       sideElems.push_back( sideElem );
8485       foundSideLinkIDs.insert ( linkID );
8486       prevSideNode = sideNode;
8487
8488       if ( *nBordIt == theBordLastNode )
8489         searchByDir = false;
8490       else {
8491         // find the next border link to compare with
8492         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8493         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8494         // move to next border node if sideNode is before forward border node (bordPos)
8495         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8496           prevBordNode = *nBordIt;
8497           nBordIt++;
8498           bordPos = nBordXYZ[ *nBordIt ];
8499           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8500           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8501         }
8502       }
8503     }
8504     while ( sideNode != theSideSecondNode );
8505
8506     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8507       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8508       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8509     }
8510   } // end nodes search on the side 2
8511
8512   // ============================
8513   // sew the border to the side 2
8514   // ============================
8515
8516   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8517   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8518
8519   TListOfListOfNodes nodeGroupsToMerge;
8520   if ( nbNodes[0] == nbNodes[1] ||
8521        ( theSideIsFreeBorder && !theSideThirdNode)) {
8522
8523     // all nodes are to be merged
8524
8525     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8526          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8527          nIt[0]++, nIt[1]++ )
8528     {
8529       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8530       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8531       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8532     }
8533   }
8534   else {
8535
8536     // insert new nodes into the border and the side to get equal nb of segments
8537
8538     // get normalized parameters of nodes on the borders
8539     //double param[ 2 ][ maxNbNodes ];
8540     double* param[ 2 ];
8541     param[0] = new double [ maxNbNodes ];
8542     param[1] = new double [ maxNbNodes ];
8543     int iNode, iBord;
8544     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8546       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8547       const SMDS_MeshNode* nPrev = *nIt;
8548       double bordLength = 0;
8549       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8550         const SMDS_MeshNode* nCur = *nIt;
8551         gp_XYZ segment (nCur->X() - nPrev->X(),
8552                         nCur->Y() - nPrev->Y(),
8553                         nCur->Z() - nPrev->Z());
8554         double segmentLen = segment.Modulus();
8555         bordLength += segmentLen;
8556         param[ iBord ][ iNode ] = bordLength;
8557         nPrev = nCur;
8558       }
8559       // normalize within [0,1]
8560       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8561         param[ iBord ][ iNode ] /= bordLength;
8562       }
8563     }
8564
8565     // loop on border segments
8566     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8567     int i[ 2 ] = { 0, 0 };
8568     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8569     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8570
8571     TElemOfNodeListMap insertMap;
8572     TElemOfNodeListMap::iterator insertMapIt;
8573     // insertMap is
8574     // key:   elem to insert nodes into
8575     // value: 2 nodes to insert between + nodes to be inserted
8576     do {
8577       bool next[ 2 ] = { false, false };
8578
8579       // find min adjacent segment length after sewing
8580       double nextParam = 10., prevParam = 0;
8581       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8582         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8583           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8584         if ( i[ iBord ] > 0 )
8585           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8586       }
8587       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8588       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8589       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8590
8591       // choose to insert or to merge nodes
8592       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8593       if ( Abs( du ) <= minSegLen * 0.2 ) {
8594         // merge
8595         // ------
8596         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8597         const SMDS_MeshNode* n0 = *nIt[0];
8598         const SMDS_MeshNode* n1 = *nIt[1];
8599         nodeGroupsToMerge.back().push_back( n1 );
8600         nodeGroupsToMerge.back().push_back( n0 );
8601         // position of node of the border changes due to merge
8602         param[ 0 ][ i[0] ] += du;
8603         // move n1 for the sake of elem shape evaluation during insertion.
8604         // n1 will be removed by MergeNodes() anyway
8605         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8606         next[0] = next[1] = true;
8607       }
8608       else {
8609         // insert
8610         // ------
8611         int intoBord = ( du < 0 ) ? 0 : 1;
8612         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8613         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8614         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8615         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8616         if ( intoBord == 1 ) {
8617           // move node of the border to be on a link of elem of the side
8618           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8619           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8620           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8621           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8622           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8623         }
8624         insertMapIt = insertMap.find( elem );
8625         bool notFound = ( insertMapIt == insertMap.end() );
8626         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8627         if ( otherLink ) {
8628           // insert into another link of the same element:
8629           // 1. perform insertion into the other link of the elem
8630           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8631           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8632           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8633           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8634           // 2. perform insertion into the link of adjacent faces
8635           while (true) {
8636             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8637             if ( adjElem )
8638               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8639             else
8640               break;
8641           }
8642           if (toCreatePolyedrs) {
8643             // perform insertion into the links of adjacent volumes
8644             UpdateVolumes(n12, n22, nodeList);
8645           }
8646           // 3. find an element appeared on n1 and n2 after the insertion
8647           insertMap.erase( elem );
8648           elem = findAdjacentFace( n1, n2, 0 );
8649         }
8650         if ( notFound || otherLink ) {
8651           // add element and nodes of the side into the insertMap
8652           insertMapIt = insertMap.insert
8653             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8654           (*insertMapIt).second.push_back( n1 );
8655           (*insertMapIt).second.push_back( n2 );
8656         }
8657         // add node to be inserted into elem
8658         (*insertMapIt).second.push_back( nIns );
8659         next[ 1 - intoBord ] = true;
8660       }
8661
8662       // go to the next segment
8663       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8664         if ( next[ iBord ] ) {
8665           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8666             eIt[ iBord ]++;
8667           nPrev[ iBord ] = *nIt[ iBord ];
8668           nIt[ iBord ]++; i[ iBord ]++;
8669         }
8670       }
8671     }
8672     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8673
8674     // perform insertion of nodes into elements
8675
8676     for (insertMapIt = insertMap.begin();
8677          insertMapIt != insertMap.end();
8678          insertMapIt++ )
8679     {
8680       const SMDS_MeshElement* elem = (*insertMapIt).first;
8681       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8682       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8683       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8684
8685       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8686
8687       if ( !theSideIsFreeBorder ) {
8688         // look for and insert nodes into the faces adjacent to elem
8689         while (true) {
8690           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8691           if ( adjElem )
8692             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8693           else
8694             break;
8695         }
8696       }
8697       if (toCreatePolyedrs) {
8698         // perform insertion into the links of adjacent volumes
8699         UpdateVolumes(n1, n2, nodeList);
8700       }
8701     }
8702
8703     delete param[0];
8704     delete param[1];
8705   } // end: insert new nodes
8706
8707   MergeNodes ( nodeGroupsToMerge );
8708
8709   return aResult;
8710 }
8711
8712 //=======================================================================
8713 //function : InsertNodesIntoLink
8714 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8715 //           and theBetweenNode2 and split theElement
8716 //=======================================================================
8717
8718 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8719                                            const SMDS_MeshNode*        theBetweenNode1,
8720                                            const SMDS_MeshNode*        theBetweenNode2,
8721                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8722                                            const bool                  toCreatePoly)
8723 {
8724   if ( theFace->GetType() != SMDSAbs_Face ) return;
8725
8726   // find indices of 2 link nodes and of the rest nodes
8727   int iNode = 0, il1, il2, i3, i4;
8728   il1 = il2 = i3 = i4 = -1;
8729   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8730   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8731
8732   if(theFace->IsQuadratic()) {
8733     const SMDS_VtkFace* F =
8734       dynamic_cast<const SMDS_VtkFace*>(theFace);
8735     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8736     // use special nodes iterator
8737     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8738     while( anIter->more() ) {
8739       const SMDS_MeshNode* n = cast2Node(anIter->next());
8740       if ( n == theBetweenNode1 )
8741         il1 = iNode;
8742       else if ( n == theBetweenNode2 )
8743         il2 = iNode;
8744       else if ( i3 < 0 )
8745         i3 = iNode;
8746       else
8747         i4 = iNode;
8748       nodes[ iNode++ ] = n;
8749     }
8750   }
8751   else {
8752     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8753     while ( nodeIt->more() ) {
8754       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8755       if ( n == theBetweenNode1 )
8756         il1 = iNode;
8757       else if ( n == theBetweenNode2 )
8758         il2 = iNode;
8759       else if ( i3 < 0 )
8760         i3 = iNode;
8761       else
8762         i4 = iNode;
8763       nodes[ iNode++ ] = n;
8764     }
8765   }
8766   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8767     return ;
8768
8769   // arrange link nodes to go one after another regarding the face orientation
8770   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8771   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8772   if ( reverse ) {
8773     iNode = il1;
8774     il1 = il2;
8775     il2 = iNode;
8776     aNodesToInsert.reverse();
8777   }
8778   // check that not link nodes of a quadrangles are in good order
8779   int nbFaceNodes = theFace->NbNodes();
8780   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8781     iNode = i3;
8782     i3 = i4;
8783     i4 = iNode;
8784   }
8785
8786   if (toCreatePoly || theFace->IsPoly()) {
8787
8788     iNode = 0;
8789     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8790
8791     // add nodes of face up to first node of link
8792     bool isFLN = false;
8793
8794     if(theFace->IsQuadratic()) {
8795       const SMDS_VtkFace* F =
8796         dynamic_cast<const SMDS_VtkFace*>(theFace);
8797       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8798       // use special nodes iterator
8799       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8800       while( anIter->more()  && !isFLN ) {
8801         const SMDS_MeshNode* n = cast2Node(anIter->next());
8802         poly_nodes[iNode++] = n;
8803         if (n == nodes[il1]) {
8804           isFLN = true;
8805         }
8806       }
8807       // add nodes to insert
8808       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8809       for (; nIt != aNodesToInsert.end(); nIt++) {
8810         poly_nodes[iNode++] = *nIt;
8811       }
8812       // add nodes of face starting from last node of link
8813       while ( anIter->more() ) {
8814         poly_nodes[iNode++] = cast2Node(anIter->next());
8815       }
8816     }
8817     else {
8818       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8819       while ( nodeIt->more() && !isFLN ) {
8820         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8821         poly_nodes[iNode++] = n;
8822         if (n == nodes[il1]) {
8823           isFLN = true;
8824         }
8825       }
8826       // add nodes to insert
8827       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8828       for (; nIt != aNodesToInsert.end(); nIt++) {
8829         poly_nodes[iNode++] = *nIt;
8830       }
8831       // add nodes of face starting from last node of link
8832       while ( nodeIt->more() ) {
8833         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8834         poly_nodes[iNode++] = n;
8835       }
8836     }
8837
8838     // edit or replace the face
8839     SMESHDS_Mesh *aMesh = GetMeshDS();
8840
8841     if (theFace->IsPoly()) {
8842       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8843     }
8844     else {
8845       int aShapeId = FindShape( theFace );
8846
8847       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8848       myLastCreatedElems.Append(newElem);
8849       if ( aShapeId && newElem )
8850         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8851
8852       aMesh->RemoveElement(theFace);
8853     }
8854     return;
8855   }
8856
8857   SMESHDS_Mesh *aMesh = GetMeshDS();
8858   if( !theFace->IsQuadratic() ) {
8859
8860     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8861     int nbLinkNodes = 2 + aNodesToInsert.size();
8862     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8863     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8864     linkNodes[ 0 ] = nodes[ il1 ];
8865     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8866     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8867     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8868       linkNodes[ iNode++ ] = *nIt;
8869     }
8870     // decide how to split a quadrangle: compare possible variants
8871     // and choose which of splits to be a quadrangle
8872     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8873     if ( nbFaceNodes == 3 ) {
8874       iBestQuad = nbSplits;
8875       i4 = i3;
8876     }
8877     else if ( nbFaceNodes == 4 ) {
8878       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8879       double aBestRate = DBL_MAX;
8880       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8881         i1 = 0; i2 = 1;
8882         double aBadRate = 0;
8883         // evaluate elements quality
8884         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8885           if ( iSplit == iQuad ) {
8886             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8887                                    linkNodes[ i2++ ],
8888                                    nodes[ i3 ],
8889                                    nodes[ i4 ]);
8890             aBadRate += getBadRate( &quad, aCrit );
8891           }
8892           else {
8893             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8894                                    linkNodes[ i2++ ],
8895                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8896             aBadRate += getBadRate( &tria, aCrit );
8897           }
8898         }
8899         // choice
8900         if ( aBadRate < aBestRate ) {
8901           iBestQuad = iQuad;
8902           aBestRate = aBadRate;
8903         }
8904       }
8905     }
8906
8907     // create new elements
8908     int aShapeId = FindShape( theFace );
8909
8910     i1 = 0; i2 = 1;
8911     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8912       SMDS_MeshElement* newElem = 0;
8913       if ( iSplit == iBestQuad )
8914         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8915                                   linkNodes[ i2++ ],
8916                                   nodes[ i3 ],
8917                                   nodes[ i4 ]);
8918       else
8919         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8920                                   linkNodes[ i2++ ],
8921                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8922       myLastCreatedElems.Append(newElem);
8923       if ( aShapeId && newElem )
8924         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8925     }
8926
8927     // change nodes of theFace
8928     const SMDS_MeshNode* newNodes[ 4 ];
8929     newNodes[ 0 ] = linkNodes[ i1 ];
8930     newNodes[ 1 ] = linkNodes[ i2 ];
8931     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8932     newNodes[ 3 ] = nodes[ i4 ];
8933     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8934     const SMDS_MeshElement* newElem = 0;
8935     if (iSplit == iBestQuad)
8936       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8937     else
8938       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8939     myLastCreatedElems.Append(newElem);
8940     if ( aShapeId && newElem )
8941       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8942 } // end if(!theFace->IsQuadratic())
8943   else { // theFace is quadratic
8944     // we have to split theFace on simple triangles and one simple quadrangle
8945     int tmp = il1/2;
8946     int nbshift = tmp*2;
8947     // shift nodes in nodes[] by nbshift
8948     int i,j;
8949     for(i=0; i<nbshift; i++) {
8950       const SMDS_MeshNode* n = nodes[0];
8951       for(j=0; j<nbFaceNodes-1; j++) {
8952         nodes[j] = nodes[j+1];
8953       }
8954       nodes[nbFaceNodes-1] = n;
8955     }
8956     il1 = il1 - nbshift;
8957     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8958     //   n0      n1     n2    n0      n1     n2
8959     //     +-----+-----+        +-----+-----+
8960     //      \         /         |           |
8961     //       \       /          |           |
8962     //      n5+     +n3       n7+           +n3
8963     //         \   /            |           |
8964     //          \ /             |           |
8965     //           +              +-----+-----+
8966     //           n4           n6      n5     n4
8967
8968     // create new elements
8969     int aShapeId = FindShape( theFace );
8970
8971     int n1,n2,n3;
8972     if(nbFaceNodes==6) { // quadratic triangle
8973       SMDS_MeshElement* newElem =
8974         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8975       myLastCreatedElems.Append(newElem);
8976       if ( aShapeId && newElem )
8977         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8978       if(theFace->IsMediumNode(nodes[il1])) {
8979         // create quadrangle
8980         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8981         myLastCreatedElems.Append(newElem);
8982         if ( aShapeId && newElem )
8983           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8984         n1 = 1;
8985         n2 = 2;
8986         n3 = 3;
8987       }
8988       else {
8989         // create quadrangle
8990         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8991         myLastCreatedElems.Append(newElem);
8992         if ( aShapeId && newElem )
8993           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8994         n1 = 0;
8995         n2 = 1;
8996         n3 = 5;
8997       }
8998     }
8999     else { // nbFaceNodes==8 - quadratic quadrangle
9000       SMDS_MeshElement* newElem =
9001         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9002       myLastCreatedElems.Append(newElem);
9003       if ( aShapeId && newElem )
9004         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9005       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9006       myLastCreatedElems.Append(newElem);
9007       if ( aShapeId && newElem )
9008         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9009       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9010       myLastCreatedElems.Append(newElem);
9011       if ( aShapeId && newElem )
9012         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9013       if(theFace->IsMediumNode(nodes[il1])) {
9014         // create quadrangle
9015         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9016         myLastCreatedElems.Append(newElem);
9017         if ( aShapeId && newElem )
9018           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9019         n1 = 1;
9020         n2 = 2;
9021         n3 = 3;
9022       }
9023       else {
9024         // create quadrangle
9025         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9026         myLastCreatedElems.Append(newElem);
9027         if ( aShapeId && newElem )
9028           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9029         n1 = 0;
9030         n2 = 1;
9031         n3 = 7;
9032       }
9033     }
9034     // create needed triangles using n1,n2,n3 and inserted nodes
9035     int nbn = 2 + aNodesToInsert.size();
9036     //const SMDS_MeshNode* aNodes[nbn];
9037     vector<const SMDS_MeshNode*> aNodes(nbn);
9038     aNodes[0] = nodes[n1];
9039     aNodes[nbn-1] = nodes[n2];
9040     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9041     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9042       aNodes[iNode++] = *nIt;
9043     }
9044     for(i=1; i<nbn; i++) {
9045       SMDS_MeshElement* newElem =
9046         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9047       myLastCreatedElems.Append(newElem);
9048       if ( aShapeId && newElem )
9049         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9050     }
9051   }
9052   // remove old face
9053   aMesh->RemoveElement(theFace);
9054 }
9055
9056 //=======================================================================
9057 //function : UpdateVolumes
9058 //purpose  :
9059 //=======================================================================
9060 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9061                                       const SMDS_MeshNode*        theBetweenNode2,
9062                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9063 {
9064   myLastCreatedElems.Clear();
9065   myLastCreatedNodes.Clear();
9066
9067   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9068   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9069     const SMDS_MeshElement* elem = invElemIt->next();
9070
9071     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9072     SMDS_VolumeTool aVolume (elem);
9073     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9074       continue;
9075
9076     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9077     int iface, nbFaces = aVolume.NbFaces();
9078     vector<const SMDS_MeshNode *> poly_nodes;
9079     vector<int> quantities (nbFaces);
9080
9081     for (iface = 0; iface < nbFaces; iface++) {
9082       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9083       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9084       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9085
9086       for (int inode = 0; inode < nbFaceNodes; inode++) {
9087         poly_nodes.push_back(faceNodes[inode]);
9088
9089         if (nbInserted == 0) {
9090           if (faceNodes[inode] == theBetweenNode1) {
9091             if (faceNodes[inode + 1] == theBetweenNode2) {
9092               nbInserted = theNodesToInsert.size();
9093
9094               // add nodes to insert
9095               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9096               for (; nIt != theNodesToInsert.end(); nIt++) {
9097                 poly_nodes.push_back(*nIt);
9098               }
9099             }
9100           }
9101           else if (faceNodes[inode] == theBetweenNode2) {
9102             if (faceNodes[inode + 1] == theBetweenNode1) {
9103               nbInserted = theNodesToInsert.size();
9104
9105               // add nodes to insert in reversed order
9106               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9107               nIt--;
9108               for (; nIt != theNodesToInsert.begin(); nIt--) {
9109                 poly_nodes.push_back(*nIt);
9110               }
9111               poly_nodes.push_back(*nIt);
9112             }
9113           }
9114           else {
9115           }
9116         }
9117       }
9118       quantities[iface] = nbFaceNodes + nbInserted;
9119     }
9120
9121     // Replace or update the volume
9122     SMESHDS_Mesh *aMesh = GetMeshDS();
9123
9124     if (elem->IsPoly()) {
9125       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9126
9127     }
9128     else {
9129       int aShapeId = FindShape( elem );
9130
9131       SMDS_MeshElement* newElem =
9132         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9133       myLastCreatedElems.Append(newElem);
9134       if (aShapeId && newElem)
9135         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9136
9137       aMesh->RemoveElement(elem);
9138     }
9139   }
9140 }
9141
9142 namespace
9143 {
9144   //================================================================================
9145   /*!
9146    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9147    */
9148   //================================================================================
9149
9150   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9151                            vector<const SMDS_MeshNode *> & nodes,
9152                            vector<int> &                   nbNodeInFaces )
9153   {
9154     nodes.clear();
9155     nbNodeInFaces.clear();
9156     SMDS_VolumeTool vTool ( elem );
9157     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9158     {
9159       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9160       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9161       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9162     }
9163   }
9164 }
9165
9166 //=======================================================================
9167 /*!
9168  * \brief Convert elements contained in a sub-mesh to quadratic
9169  * \return int - nb of checked elements
9170  */
9171 //=======================================================================
9172
9173 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9174                                              SMESH_MesherHelper& theHelper,
9175                                              const bool          theForce3d)
9176 {
9177   int nbElem = 0;
9178   if( !theSm ) return nbElem;
9179
9180   vector<int> nbNodeInFaces;
9181   vector<const SMDS_MeshNode *> nodes;
9182   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9183   while(ElemItr->more())
9184   {
9185     nbElem++;
9186     const SMDS_MeshElement* elem = ElemItr->next();
9187     if( !elem ) continue;
9188
9189     // analyse a necessity of conversion
9190     const SMDSAbs_ElementType aType = elem->GetType();
9191     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9192       continue;
9193     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9194     bool hasCentralNodes = false;
9195     if ( elem->IsQuadratic() )
9196     {
9197       bool alreadyOK;
9198       switch ( aGeomType ) {
9199       case SMDSEntity_Quad_Triangle:
9200       case SMDSEntity_Quad_Quadrangle:
9201       case SMDSEntity_Quad_Hexa:
9202         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9203
9204       case SMDSEntity_BiQuad_Triangle:
9205       case SMDSEntity_BiQuad_Quadrangle:
9206       case SMDSEntity_TriQuad_Hexa:
9207         alreadyOK = theHelper.GetIsBiQuadratic();
9208         hasCentralNodes = true;
9209         break;
9210       default:
9211         alreadyOK = true;
9212       }
9213       // take into account already present modium nodes
9214       switch ( aType ) {
9215       case SMDSAbs_Volume:
9216         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9217       case SMDSAbs_Face:
9218         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9219       case SMDSAbs_Edge:
9220         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9221       default:;
9222       }
9223       if ( alreadyOK )
9224         continue;
9225     }
9226     // get elem data needed to re-create it
9227     //
9228     const int id      = elem->GetID();
9229     const int nbNodes = elem->NbCornerNodes();
9230     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9231     if ( aGeomType == SMDSEntity_Polyhedra )
9232       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9233     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9234       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9235
9236     // remove a linear element
9237     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9238
9239     // remove central nodes of biquadratic elements (biquad->quad convertion)
9240     if ( hasCentralNodes )
9241       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9242         if ( nodes[i]->NbInverseElements() == 0 )
9243           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9244
9245     const SMDS_MeshElement* NewElem = 0;
9246
9247     switch( aType )
9248     {
9249     case SMDSAbs_Edge :
9250       {
9251         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9252         break;
9253       }
9254     case SMDSAbs_Face :
9255       {
9256         switch(nbNodes)
9257         {
9258         case 3:
9259           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9260           break;
9261         case 4:
9262           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9263           break;
9264         default:
9265           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9266         }
9267         break;
9268       }
9269     case SMDSAbs_Volume :
9270       {
9271         switch( aGeomType )
9272         {
9273         case SMDSEntity_Tetra:
9274           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9275           break;
9276         case SMDSEntity_Pyramid:
9277           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9278           break;
9279         case SMDSEntity_Penta:
9280           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9281           break;
9282         case SMDSEntity_Hexa:
9283         case SMDSEntity_Quad_Hexa:
9284         case SMDSEntity_TriQuad_Hexa:
9285           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9286                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9287           break;
9288         case SMDSEntity_Hexagonal_Prism:
9289         default:
9290           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9291         }
9292         break;
9293       }
9294     default :
9295       continue;
9296     }
9297     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9298     if( NewElem && NewElem->getshapeId() < 1 )
9299       theSm->AddElement( NewElem );
9300   }
9301   return nbElem;
9302 }
9303 //=======================================================================
9304 //function : ConvertToQuadratic
9305 //purpose  :
9306 //=======================================================================
9307
9308 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9309 {
9310   SMESHDS_Mesh* meshDS = GetMeshDS();
9311
9312   SMESH_MesherHelper aHelper(*myMesh);
9313
9314   aHelper.SetIsQuadratic( true );
9315   aHelper.SetIsBiQuadratic( theToBiQuad );
9316   aHelper.SetElementsOnShape(true);
9317   aHelper.ToFixNodeParameters( true );
9318
9319   // convert elements assigned to sub-meshes
9320   int nbCheckedElems = 0;
9321   if ( myMesh->HasShapeToMesh() )
9322   {
9323     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9324     {
9325       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9326       while ( smIt->more() ) {
9327         SMESH_subMesh* sm = smIt->next();
9328         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9329           aHelper.SetSubShape( sm->GetSubShape() );
9330           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9331         }
9332       }
9333     }
9334   }
9335
9336   // convert elements NOT assigned to sub-meshes
9337   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9338   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9339   {
9340     aHelper.SetElementsOnShape(false);
9341     SMESHDS_SubMesh *smDS = 0;
9342
9343     // convert edges
9344     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9345     while( aEdgeItr->more() )
9346     {
9347       const SMDS_MeshEdge* edge = aEdgeItr->next();
9348       if ( !edge->IsQuadratic() )
9349       {
9350         int                  id = edge->GetID();
9351         const SMDS_MeshNode* n1 = edge->GetNode(0);
9352         const SMDS_MeshNode* n2 = edge->GetNode(1);
9353
9354         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9355
9356         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9357         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9358       }
9359       else
9360       {
9361         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9362       }
9363     }
9364
9365     // convert faces
9366     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9367     while( aFaceItr->more() )
9368     {
9369       const SMDS_MeshFace* face = aFaceItr->next();
9370       if ( !face ) continue;
9371       
9372       const SMDSAbs_EntityType type = face->GetEntityType();
9373       bool alreadyOK;
9374       switch( type )
9375       {
9376       case SMDSEntity_Quad_Triangle:
9377       case SMDSEntity_Quad_Quadrangle:
9378         alreadyOK = !theToBiQuad;
9379         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9380         break;
9381       case SMDSEntity_BiQuad_Triangle:
9382       case SMDSEntity_BiQuad_Quadrangle:
9383         alreadyOK = theToBiQuad;
9384         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9385         break;
9386       default: alreadyOK = false;
9387       }
9388       if ( alreadyOK )
9389         continue;
9390
9391       const int id = face->GetID();
9392       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9393
9394       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9395
9396       SMDS_MeshFace * NewFace = 0;
9397       switch( type )
9398       {
9399       case SMDSEntity_Triangle:
9400       case SMDSEntity_Quad_Triangle:
9401       case SMDSEntity_BiQuad_Triangle:
9402         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9403         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9404           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9405         break;
9406
9407       case SMDSEntity_Quadrangle:
9408       case SMDSEntity_Quad_Quadrangle:
9409       case SMDSEntity_BiQuad_Quadrangle:
9410         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9411         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9412           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9413         break;
9414
9415       default:;
9416         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9417       }
9418       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9419     }
9420
9421     // convert volumes
9422     vector<int> nbNodeInFaces;
9423     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9424     while(aVolumeItr->more())
9425     {
9426       const SMDS_MeshVolume* volume = aVolumeItr->next();
9427       if ( !volume ) continue;
9428
9429       const SMDSAbs_EntityType type = volume->GetEntityType();
9430       if ( volume->IsQuadratic() )
9431       {
9432         bool alreadyOK;
9433         switch ( type )
9434         {
9435         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9436         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9437         default:                      alreadyOK = true;
9438         }
9439         if ( alreadyOK )
9440         {
9441           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9442           continue;
9443         }
9444       }
9445       const int id = volume->GetID();
9446       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9447       if ( type == SMDSEntity_Polyhedra )
9448         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9449       else if ( type == SMDSEntity_Hexagonal_Prism )
9450         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9451
9452       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9453
9454       SMDS_MeshVolume * NewVolume = 0;
9455       switch ( type )
9456       {
9457       case SMDSEntity_Tetra:
9458         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9459         break;
9460       case SMDSEntity_Hexa:
9461       case SMDSEntity_Quad_Hexa:
9462       case SMDSEntity_TriQuad_Hexa:
9463         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9464                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9465         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9466           if ( nodes[i]->NbInverseElements() == 0 )
9467             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9468         break;
9469       case SMDSEntity_Pyramid:
9470         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9471                                       nodes[3], nodes[4], id, theForce3d);
9472         break;
9473       case SMDSEntity_Penta:
9474         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9475                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9476         break;
9477       case SMDSEntity_Hexagonal_Prism:
9478       default:
9479         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9480       }
9481       ReplaceElemInGroups(volume, NewVolume, meshDS);
9482     }
9483   }
9484
9485   if ( !theForce3d )
9486   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9487     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9488     // aHelper.FixQuadraticElements(myError);
9489     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9490   }
9491 }
9492
9493 //================================================================================
9494 /*!
9495  * \brief Makes given elements quadratic
9496  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9497  *  \param theElements - elements to make quadratic
9498  */
9499 //================================================================================
9500
9501 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9502                                           TIDSortedElemSet& theElements,
9503                                           const bool        theToBiQuad)
9504 {
9505   if ( theElements.empty() ) return;
9506
9507   // we believe that all theElements are of the same type
9508   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9509
9510   // get all nodes shared by theElements
9511   TIDSortedNodeSet allNodes;
9512   TIDSortedElemSet::iterator eIt = theElements.begin();
9513   for ( ; eIt != theElements.end(); ++eIt )
9514     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9515
9516   // complete theElements with elements of lower dim whose all nodes are in allNodes
9517
9518   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9519   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9520   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9521   for ( ; nIt != allNodes.end(); ++nIt )
9522   {
9523     const SMDS_MeshNode* n = *nIt;
9524     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9525     while ( invIt->more() )
9526     {
9527       const SMDS_MeshElement*      e = invIt->next();
9528       const SMDSAbs_ElementType type = e->GetType();
9529       if ( e->IsQuadratic() )
9530       {
9531         quadAdjacentElems[ type ].insert( e );
9532
9533         bool alreadyOK;
9534         switch ( e->GetEntityType() ) {
9535         case SMDSEntity_Quad_Triangle:
9536         case SMDSEntity_Quad_Quadrangle:
9537         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9538         case SMDSEntity_BiQuad_Triangle:
9539         case SMDSEntity_BiQuad_Quadrangle:
9540         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9541         default:                           alreadyOK = true;
9542         }
9543         if ( alreadyOK )
9544           continue;
9545       }
9546       if ( type >= elemType )
9547         continue; // same type or more complex linear element
9548
9549       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9550         continue; // e is already checked
9551
9552       // check nodes
9553       bool allIn = true;
9554       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9555       while ( nodeIt->more() && allIn )
9556         allIn = allNodes.count( nodeIt->next() );
9557       if ( allIn )
9558         theElements.insert(e );
9559     }
9560   }
9561
9562   SMESH_MesherHelper helper(*myMesh);
9563   helper.SetIsQuadratic( true );
9564   helper.SetIsBiQuadratic( theToBiQuad );
9565
9566   // add links of quadratic adjacent elements to the helper
9567
9568   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9569     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9570           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9571     {
9572       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9573     }
9574   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9575     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9576           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9577     {
9578       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9579     }
9580   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9581     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9582           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9583     {
9584       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9585     }
9586
9587   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9588
9589   SMESHDS_Mesh*  meshDS = GetMeshDS();
9590   SMESHDS_SubMesh* smDS = 0;
9591   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9592   {
9593     const SMDS_MeshElement* elem = *eIt;
9594
9595     bool alreadyOK;
9596     int nbCentralNodes = 0;
9597     switch ( elem->GetEntityType() ) {
9598       // linear convertible
9599     case SMDSEntity_Edge:
9600     case SMDSEntity_Triangle:
9601     case SMDSEntity_Quadrangle:
9602     case SMDSEntity_Tetra:
9603     case SMDSEntity_Pyramid:
9604     case SMDSEntity_Hexa:
9605     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9606       // quadratic that can become bi-quadratic
9607     case SMDSEntity_Quad_Triangle:
9608     case SMDSEntity_Quad_Quadrangle:
9609     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9610       // bi-quadratic
9611     case SMDSEntity_BiQuad_Triangle:
9612     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9613     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9614       // the rest
9615     default:                           alreadyOK = true;
9616     }
9617     if ( alreadyOK ) continue;
9618
9619     const SMDSAbs_ElementType type = elem->GetType();
9620     const int                   id = elem->GetID();
9621     const int              nbNodes = elem->NbCornerNodes();
9622     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9623
9624     helper.SetSubShape( elem->getshapeId() );
9625
9626     if ( !smDS || !smDS->Contains( elem ))
9627       smDS = meshDS->MeshElements( elem->getshapeId() );
9628     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9629
9630     SMDS_MeshElement * newElem = 0;
9631     switch( nbNodes )
9632     {
9633     case 4: // cases for most frequently used element types go first (for optimization)
9634       if ( type == SMDSAbs_Volume )
9635         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9636       else
9637         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9638       break;
9639     case 8:
9640       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9641                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9642       break;
9643     case 3:
9644       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9645       break;
9646     case 2:
9647       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9648       break;
9649     case 5:
9650       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9651                                  nodes[4], id, theForce3d);
9652       break;
9653     case 6:
9654       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9655                                  nodes[4], nodes[5], id, theForce3d);
9656       break;
9657     default:;
9658     }
9659     ReplaceElemInGroups( elem, newElem, meshDS);
9660     if( newElem && smDS )
9661       smDS->AddElement( newElem );
9662
9663      // remove central nodes
9664     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9665       if ( nodes[i]->NbInverseElements() == 0 )
9666         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9667
9668   } // loop on theElements
9669
9670   if ( !theForce3d )
9671   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9672     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9673     // helper.FixQuadraticElements( myError );
9674     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9675   }
9676 }
9677
9678 //=======================================================================
9679 /*!
9680  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9681  * \return int - nb of checked elements
9682  */
9683 //=======================================================================
9684
9685 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9686                                      SMDS_ElemIteratorPtr theItr,
9687                                      const int            theShapeID)
9688 {
9689   int nbElem = 0;
9690   SMESHDS_Mesh* meshDS = GetMeshDS();
9691   ElemFeatures elemType;
9692   vector<const SMDS_MeshNode *> nodes;
9693
9694   while( theItr->more() )
9695   {
9696     const SMDS_MeshElement* elem = theItr->next();
9697     nbElem++;
9698     if( elem && elem->IsQuadratic())
9699     {
9700       // get elem data
9701       int nbCornerNodes = elem->NbCornerNodes();
9702       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9703
9704       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9705
9706       //remove a quadratic element
9707       if ( !theSm || !theSm->Contains( elem ))
9708         theSm = meshDS->MeshElements( elem->getshapeId() );
9709       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9710
9711       // remove medium nodes
9712       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9713         if ( nodes[i]->NbInverseElements() == 0 )
9714           meshDS->RemoveFreeNode( nodes[i], theSm );
9715
9716       // add a linear element
9717       nodes.resize( nbCornerNodes );
9718       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9719       ReplaceElemInGroups(elem, newElem, meshDS);
9720       if( theSm && newElem )
9721         theSm->AddElement( newElem );
9722     }
9723   }
9724   return nbElem;
9725 }
9726
9727 //=======================================================================
9728 //function : ConvertFromQuadratic
9729 //purpose  :
9730 //=======================================================================
9731
9732 bool SMESH_MeshEditor::ConvertFromQuadratic()
9733 {
9734   int nbCheckedElems = 0;
9735   if ( myMesh->HasShapeToMesh() )
9736   {
9737     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9738     {
9739       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9740       while ( smIt->more() ) {
9741         SMESH_subMesh* sm = smIt->next();
9742         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9743           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9744       }
9745     }
9746   }
9747
9748   int totalNbElems =
9749     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9750   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9751   {
9752     SMESHDS_SubMesh *aSM = 0;
9753     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9754   }
9755
9756   return true;
9757 }
9758
9759 namespace
9760 {
9761   //================================================================================
9762   /*!
9763    * \brief Return true if all medium nodes of the element are in the node set
9764    */
9765   //================================================================================
9766
9767   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9768   {
9769     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9770       if ( !nodeSet.count( elem->GetNode(i) ))
9771         return false;
9772     return true;
9773   }
9774 }
9775
9776 //================================================================================
9777 /*!
9778  * \brief Makes given elements linear
9779  */
9780 //================================================================================
9781
9782 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9783 {
9784   if ( theElements.empty() ) return;
9785
9786   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9787   set<int> mediumNodeIDs;
9788   TIDSortedElemSet::iterator eIt = theElements.begin();
9789   for ( ; eIt != theElements.end(); ++eIt )
9790   {
9791     const SMDS_MeshElement* e = *eIt;
9792     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9793       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9794   }
9795
9796   // replace given elements by linear ones
9797   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9798   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9799
9800   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9801   // except those elements sharing medium nodes of quadratic element whose medium nodes
9802   // are not all in mediumNodeIDs
9803
9804   // get remaining medium nodes
9805   TIDSortedNodeSet mediumNodes;
9806   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9807   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9808     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9809       mediumNodes.insert( mediumNodes.end(), n );
9810
9811   // find more quadratic elements to convert
9812   TIDSortedElemSet moreElemsToConvert;
9813   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9814   for ( ; nIt != mediumNodes.end(); ++nIt )
9815   {
9816     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9817     while ( invIt->more() )
9818     {
9819       const SMDS_MeshElement* e = invIt->next();
9820       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9821       {
9822         // find a more complex element including e and
9823         // whose medium nodes are not in mediumNodes
9824         bool complexFound = false;
9825         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9826         {
9827           SMDS_ElemIteratorPtr invIt2 =
9828             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9829           while ( invIt2->more() )
9830           {
9831             const SMDS_MeshElement* eComplex = invIt2->next();
9832             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9833             {
9834               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9835               if ( nbCommonNodes == e->NbNodes())
9836               {
9837                 complexFound = true;
9838                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9839                 break;
9840               }
9841             }
9842           }
9843         }
9844         if ( !complexFound )
9845           moreElemsToConvert.insert( e );
9846       }
9847     }
9848   }
9849   elemIt = elemSetIterator( moreElemsToConvert );
9850   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9851 }
9852
9853 //=======================================================================
9854 //function : SewSideElements
9855 //purpose  :
9856 //=======================================================================
9857
9858 SMESH_MeshEditor::Sew_Error
9859 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9860                                    TIDSortedElemSet&    theSide2,
9861                                    const SMDS_MeshNode* theFirstNode1,
9862                                    const SMDS_MeshNode* theFirstNode2,
9863                                    const SMDS_MeshNode* theSecondNode1,
9864                                    const SMDS_MeshNode* theSecondNode2)
9865 {
9866   myLastCreatedElems.Clear();
9867   myLastCreatedNodes.Clear();
9868
9869   MESSAGE ("::::SewSideElements()");
9870   if ( theSide1.size() != theSide2.size() )
9871     return SEW_DIFF_NB_OF_ELEMENTS;
9872
9873   Sew_Error aResult = SEW_OK;
9874   // Algo:
9875   // 1. Build set of faces representing each side
9876   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9877   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9878
9879   // =======================================================================
9880   // 1. Build set of faces representing each side:
9881   // =======================================================================
9882   // a. build set of nodes belonging to faces
9883   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9884   // c. create temporary faces representing side of volumes if correspondent
9885   //    face does not exist
9886
9887   SMESHDS_Mesh* aMesh = GetMeshDS();
9888   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9889   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9890   TIDSortedElemSet             faceSet1, faceSet2;
9891   set<const SMDS_MeshElement*> volSet1,  volSet2;
9892   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9893   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9894   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9895   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9896   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9897   int iSide, iFace, iNode;
9898
9899   list<const SMDS_MeshElement* > tempFaceList;
9900   for ( iSide = 0; iSide < 2; iSide++ ) {
9901     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9902     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9903     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9904     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9905     set<const SMDS_MeshElement*>::iterator vIt;
9906     TIDSortedElemSet::iterator eIt;
9907     set<const SMDS_MeshNode*>::iterator    nIt;
9908
9909     // check that given nodes belong to given elements
9910     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9911     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9912     int firstIndex = -1, secondIndex = -1;
9913     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9914       const SMDS_MeshElement* elem = *eIt;
9915       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9916       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9917       if ( firstIndex > -1 && secondIndex > -1 ) break;
9918     }
9919     if ( firstIndex < 0 || secondIndex < 0 ) {
9920       // we can simply return until temporary faces created
9921       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9922     }
9923
9924     // -----------------------------------------------------------
9925     // 1a. Collect nodes of existing faces
9926     //     and build set of face nodes in order to detect missing
9927     //     faces corresponding to sides of volumes
9928     // -----------------------------------------------------------
9929
9930     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9931
9932     // loop on the given element of a side
9933     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9934       //const SMDS_MeshElement* elem = *eIt;
9935       const SMDS_MeshElement* elem = *eIt;
9936       if ( elem->GetType() == SMDSAbs_Face ) {
9937         faceSet->insert( elem );
9938         set <const SMDS_MeshNode*> faceNodeSet;
9939         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9940         while ( nodeIt->more() ) {
9941           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9942           nodeSet->insert( n );
9943           faceNodeSet.insert( n );
9944         }
9945         setOfFaceNodeSet.insert( faceNodeSet );
9946       }
9947       else if ( elem->GetType() == SMDSAbs_Volume )
9948         volSet->insert( elem );
9949     }
9950     // ------------------------------------------------------------------------------
9951     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9952     // ------------------------------------------------------------------------------
9953
9954     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9955       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9956       while ( fIt->more() ) { // loop on faces sharing a node
9957         const SMDS_MeshElement* f = fIt->next();
9958         if ( faceSet->find( f ) == faceSet->end() ) {
9959           // check if all nodes are in nodeSet and
9960           // complete setOfFaceNodeSet if they are
9961           set <const SMDS_MeshNode*> faceNodeSet;
9962           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9963           bool allInSet = true;
9964           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9965             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966             if ( nodeSet->find( n ) == nodeSet->end() )
9967               allInSet = false;
9968             else
9969               faceNodeSet.insert( n );
9970           }
9971           if ( allInSet ) {
9972             faceSet->insert( f );
9973             setOfFaceNodeSet.insert( faceNodeSet );
9974           }
9975         }
9976       }
9977     }
9978
9979     // -------------------------------------------------------------------------
9980     // 1c. Create temporary faces representing sides of volumes if correspondent
9981     //     face does not exist
9982     // -------------------------------------------------------------------------
9983
9984     if ( !volSet->empty() ) {
9985       //int nodeSetSize = nodeSet->size();
9986
9987       // loop on given volumes
9988       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9989         SMDS_VolumeTool vol (*vIt);
9990         // loop on volume faces: find free faces
9991         // --------------------------------------
9992         list<const SMDS_MeshElement* > freeFaceList;
9993         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9994           if ( !vol.IsFreeFace( iFace ))
9995             continue;
9996           // check if there is already a face with same nodes in a face set
9997           const SMDS_MeshElement* aFreeFace = 0;
9998           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9999           int nbNodes = vol.NbFaceNodes( iFace );
10000           set <const SMDS_MeshNode*> faceNodeSet;
10001           vol.GetFaceNodes( iFace, faceNodeSet );
10002           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10003           if ( isNewFace ) {
10004             // no such a face is given but it still can exist, check it
10005             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10006             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10007           }
10008           if ( !aFreeFace ) {
10009             // create a temporary face
10010             if ( nbNodes == 3 ) {
10011               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10012               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10013             }
10014             else if ( nbNodes == 4 ) {
10015               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10016               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10017             }
10018             else {
10019               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10020               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10021               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10022             }
10023             if ( aFreeFace )
10024               tempFaceList.push_back( aFreeFace );
10025           }
10026
10027           if ( aFreeFace )
10028             freeFaceList.push_back( aFreeFace );
10029
10030         } // loop on faces of a volume
10031
10032         // choose one of several free faces of a volume
10033         // --------------------------------------------
10034         if ( freeFaceList.size() > 1 ) {
10035           // choose a face having max nb of nodes shared by other elems of a side
10036           int maxNbNodes = -1;
10037           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10038           while ( fIt != freeFaceList.end() ) { // loop on free faces
10039             int nbSharedNodes = 0;
10040             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10041             while ( nodeIt->more() ) { // loop on free face nodes
10042               const SMDS_MeshNode* n =
10043                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10044               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10045               while ( invElemIt->more() ) {
10046                 const SMDS_MeshElement* e = invElemIt->next();
10047                 nbSharedNodes += faceSet->count( e );
10048                 nbSharedNodes += elemSet->count( e );
10049               }
10050             }
10051             if ( nbSharedNodes > maxNbNodes ) {
10052               maxNbNodes = nbSharedNodes;
10053               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10054             }
10055             else if ( nbSharedNodes == maxNbNodes ) {
10056               fIt++;
10057             }
10058             else {
10059               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10060             }
10061           }
10062           if ( freeFaceList.size() > 1 )
10063           {
10064             // could not choose one face, use another way
10065             // choose a face most close to the bary center of the opposite side
10066             gp_XYZ aBC( 0., 0., 0. );
10067             set <const SMDS_MeshNode*> addedNodes;
10068             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10069             eIt = elemSet2->begin();
10070             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10071               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10072               while ( nodeIt->more() ) { // loop on free face nodes
10073                 const SMDS_MeshNode* n =
10074                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10075                 if ( addedNodes.insert( n ).second )
10076                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10077               }
10078             }
10079             aBC /= addedNodes.size();
10080             double minDist = DBL_MAX;
10081             fIt = freeFaceList.begin();
10082             while ( fIt != freeFaceList.end() ) { // loop on free faces
10083               double dist = 0;
10084               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10085               while ( nodeIt->more() ) { // loop on free face nodes
10086                 const SMDS_MeshNode* n =
10087                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10088                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10089                 dist += ( aBC - p ).SquareModulus();
10090               }
10091               if ( dist < minDist ) {
10092                 minDist = dist;
10093                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10094               }
10095               else
10096                 fIt = freeFaceList.erase( fIt++ );
10097             }
10098           }
10099         } // choose one of several free faces of a volume
10100
10101         if ( freeFaceList.size() == 1 ) {
10102           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10103           faceSet->insert( aFreeFace );
10104           // complete a node set with nodes of a found free face
10105           //           for ( iNode = 0; iNode < ; iNode++ )
10106           //             nodeSet->insert( fNodes[ iNode ] );
10107         }
10108
10109       } // loop on volumes of a side
10110
10111       //       // complete a set of faces if new nodes in a nodeSet appeared
10112       //       // ----------------------------------------------------------
10113       //       if ( nodeSetSize != nodeSet->size() ) {
10114       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10115       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10116       //           while ( fIt->more() ) { // loop on faces sharing a node
10117       //             const SMDS_MeshElement* f = fIt->next();
10118       //             if ( faceSet->find( f ) == faceSet->end() ) {
10119       //               // check if all nodes are in nodeSet and
10120       //               // complete setOfFaceNodeSet if they are
10121       //               set <const SMDS_MeshNode*> faceNodeSet;
10122       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10123       //               bool allInSet = true;
10124       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10125       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10126       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10127       //                   allInSet = false;
10128       //                 else
10129       //                   faceNodeSet.insert( n );
10130       //               }
10131       //               if ( allInSet ) {
10132       //                 faceSet->insert( f );
10133       //                 setOfFaceNodeSet.insert( faceNodeSet );
10134       //               }
10135       //             }
10136       //           }
10137       //         }
10138       //       }
10139     } // Create temporary faces, if there are volumes given
10140   } // loop on sides
10141
10142   if ( faceSet1.size() != faceSet2.size() ) {
10143     // delete temporary faces: they are in reverseElements of actual nodes
10144 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10145 //    while ( tmpFaceIt->more() )
10146 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10147 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10148 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10149 //      aMesh->RemoveElement(*tmpFaceIt);
10150     MESSAGE("Diff nb of faces");
10151     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10152   }
10153
10154   // ============================================================
10155   // 2. Find nodes to merge:
10156   //              bind a node to remove to a node to put instead
10157   // ============================================================
10158
10159   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10160   if ( theFirstNode1 != theFirstNode2 )
10161     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10162   if ( theSecondNode1 != theSecondNode2 )
10163     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10164
10165   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10166   set< long > linkIdSet; // links to process
10167   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10168
10169   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10170   list< NLink > linkList[2];
10171   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10172   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10173   // loop on links in linkList; find faces by links and append links
10174   // of the found faces to linkList
10175   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10176   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10177   {
10178     NLink link[] = { *linkIt[0], *linkIt[1] };
10179     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10180     if ( !linkIdSet.count( linkID ) )
10181       continue;
10182
10183     // by links, find faces in the face sets,
10184     // and find indices of link nodes in the found faces;
10185     // in a face set, there is only one or no face sharing a link
10186     // ---------------------------------------------------------------
10187
10188     const SMDS_MeshElement* face[] = { 0, 0 };
10189     vector<const SMDS_MeshNode*> fnodes[2];
10190     int iLinkNode[2][2];
10191     TIDSortedElemSet avoidSet;
10192     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10193       const SMDS_MeshNode* n1 = link[iSide].first;
10194       const SMDS_MeshNode* n2 = link[iSide].second;
10195       //cout << "Side " << iSide << " ";
10196       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10197       // find a face by two link nodes
10198       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10199                                                       *faceSetPtr[ iSide ], avoidSet,
10200                                                       &iLinkNode[iSide][0],
10201                                                       &iLinkNode[iSide][1] );
10202       if ( face[ iSide ])
10203       {
10204         //cout << " F " << face[ iSide]->GetID() <<endl;
10205         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10206         // put face nodes to fnodes
10207         if ( face[ iSide ]->IsQuadratic() )
10208         {
10209           // use interlaced nodes iterator
10210           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10211           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10212           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10213           while ( nIter->more() )
10214             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10215         }
10216         else
10217         {
10218           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10219                                   face[ iSide ]->end_nodes() );
10220         }
10221         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10222       }
10223     }
10224
10225     // check similarity of elements of the sides
10226     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10227       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10228       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10229         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10230       }
10231       else {
10232         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10233       }
10234       break; // do not return because it's necessary to remove tmp faces
10235     }
10236
10237     // set nodes to merge
10238     // -------------------
10239
10240     if ( face[0] && face[1] )  {
10241       const int nbNodes = face[0]->NbNodes();
10242       if ( nbNodes != face[1]->NbNodes() ) {
10243         MESSAGE("Diff nb of face nodes");
10244         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10245         break; // do not return because it s necessary to remove tmp faces
10246       }
10247       bool reverse[] = { false, false }; // order of nodes in the link
10248       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10249         // analyse link orientation in faces
10250         int i1 = iLinkNode[ iSide ][ 0 ];
10251         int i2 = iLinkNode[ iSide ][ 1 ];
10252         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10253       }
10254       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10255       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10256       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10257       {
10258         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10259                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10260       }
10261
10262       // add other links of the faces to linkList
10263       // -----------------------------------------
10264
10265       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10266         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10267         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10268         if ( !iter_isnew.second ) { // already in a set: no need to process
10269           linkIdSet.erase( iter_isnew.first );
10270         }
10271         else // new in set == encountered for the first time: add
10272         {
10273           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10274           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10275           linkList[0].push_back ( NLink( n1, n2 ));
10276           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10277         }
10278       }
10279     } // 2 faces found
10280
10281     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10282       break;
10283
10284   } // loop on link lists
10285
10286   if ( aResult == SEW_OK &&
10287        ( //linkIt[0] != linkList[0].end() ||
10288          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10289     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10290              " " << (faceSetPtr[1]->empty()));
10291     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10292   }
10293
10294   // ====================================================================
10295   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10296   // ====================================================================
10297
10298   // delete temporary faces
10299 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10300 //  while ( tmpFaceIt->more() )
10301 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10302   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10303   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10304     aMesh->RemoveElement(*tmpFaceIt);
10305
10306   if ( aResult != SEW_OK)
10307     return aResult;
10308
10309   list< int > nodeIDsToRemove;
10310   vector< const SMDS_MeshNode*> nodes;
10311   ElemFeatures elemType;
10312
10313   // loop on nodes replacement map
10314   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10315   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10316     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10317     {
10318       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10319       nodeIDsToRemove.push_back( nToRemove->GetID() );
10320       // loop on elements sharing nToRemove
10321       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10322       while ( invElemIt->more() ) {
10323         const SMDS_MeshElement* e = invElemIt->next();
10324         // get a new suite of nodes: make replacement
10325         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10326         nodes.resize( nbNodes );
10327         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10328         while ( nIt->more() ) {
10329           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10330           nnIt = nReplaceMap.find( n );
10331           if ( nnIt != nReplaceMap.end() ) {
10332             nbReplaced++;
10333             n = (*nnIt).second;
10334           }
10335           nodes[ i++ ] = n;
10336         }
10337         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10338         //         elemIDsToRemove.push_back( e->GetID() );
10339         //       else
10340         if ( nbReplaced )
10341         {
10342           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10343           aMesh->RemoveElement( e );
10344
10345           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10346           {
10347             AddToSameGroups( newElem, e, aMesh );
10348             if ( int aShapeId = e->getshapeId() )
10349               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10350           }
10351         }
10352       }
10353     }
10354
10355   Remove( nodeIDsToRemove, true );
10356
10357   return aResult;
10358 }
10359
10360 //================================================================================
10361 /*!
10362  * \brief Find corresponding nodes in two sets of faces
10363  * \param theSide1 - first face set
10364  * \param theSide2 - second first face
10365  * \param theFirstNode1 - a boundary node of set 1
10366  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10367  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10368  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10369  * \param nReplaceMap - output map of corresponding nodes
10370  * \return bool  - is a success or not
10371  */
10372 //================================================================================
10373
10374 #ifdef _DEBUG_
10375 //#define DEBUG_MATCHING_NODES
10376 #endif
10377
10378 SMESH_MeshEditor::Sew_Error
10379 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10380                                     set<const SMDS_MeshElement*>& theSide2,
10381                                     const SMDS_MeshNode*          theFirstNode1,
10382                                     const SMDS_MeshNode*          theFirstNode2,
10383                                     const SMDS_MeshNode*          theSecondNode1,
10384                                     const SMDS_MeshNode*          theSecondNode2,
10385                                     TNodeNodeMap &                nReplaceMap)
10386 {
10387   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10388
10389   nReplaceMap.clear();
10390   if ( theFirstNode1 != theFirstNode2 )
10391     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10392   if ( theSecondNode1 != theSecondNode2 )
10393     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10394
10395   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10396   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10397
10398   list< NLink > linkList[2];
10399   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10400   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10401
10402   // loop on links in linkList; find faces by links and append links
10403   // of the found faces to linkList
10404   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10405   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10406     NLink link[] = { *linkIt[0], *linkIt[1] };
10407     if ( linkSet.find( link[0] ) == linkSet.end() )
10408       continue;
10409
10410     // by links, find faces in the face sets,
10411     // and find indices of link nodes in the found faces;
10412     // in a face set, there is only one or no face sharing a link
10413     // ---------------------------------------------------------------
10414
10415     const SMDS_MeshElement* face[] = { 0, 0 };
10416     list<const SMDS_MeshNode*> notLinkNodes[2];
10417     //bool reverse[] = { false, false }; // order of notLinkNodes
10418     int nbNodes[2];
10419     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10420     {
10421       const SMDS_MeshNode* n1 = link[iSide].first;
10422       const SMDS_MeshNode* n2 = link[iSide].second;
10423       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10424       set< const SMDS_MeshElement* > facesOfNode1;
10425       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10426       {
10427         // during a loop of the first node, we find all faces around n1,
10428         // during a loop of the second node, we find one face sharing both n1 and n2
10429         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10430         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10431         while ( fIt->more() ) { // loop on faces sharing a node
10432           const SMDS_MeshElement* f = fIt->next();
10433           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10434               ! facesOfNode1.insert( f ).second ) // f encounters twice
10435           {
10436             if ( face[ iSide ] ) {
10437               MESSAGE( "2 faces per link " );
10438               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10439             }
10440             face[ iSide ] = f;
10441             faceSet->erase( f );
10442
10443             // get not link nodes
10444             int nbN = f->NbNodes();
10445             if ( f->IsQuadratic() )
10446               nbN /= 2;
10447             nbNodes[ iSide ] = nbN;
10448             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10449             int i1 = f->GetNodeIndex( n1 );
10450             int i2 = f->GetNodeIndex( n2 );
10451             int iEnd = nbN, iBeg = -1, iDelta = 1;
10452             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10453             if ( reverse ) {
10454               std::swap( iEnd, iBeg ); iDelta = -1;
10455             }
10456             int i = i2;
10457             while ( true ) {
10458               i += iDelta;
10459               if ( i == iEnd ) i = iBeg + iDelta;
10460               if ( i == i1 ) break;
10461               nodes.push_back ( f->GetNode( i ) );
10462             }
10463           }
10464         }
10465       }
10466     }
10467     // check similarity of elements of the sides
10468     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10469       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10470       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10471         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10472       }
10473       else {
10474         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10475       }
10476     }
10477
10478     // set nodes to merge
10479     // -------------------
10480
10481     if ( face[0] && face[1] )  {
10482       if ( nbNodes[0] != nbNodes[1] ) {
10483         MESSAGE("Diff nb of face nodes");
10484         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10485       }
10486 #ifdef DEBUG_MATCHING_NODES
10487       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10488                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10489                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10490 #endif
10491       int nbN = nbNodes[0];
10492       {
10493         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10494         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10495         for ( int i = 0 ; i < nbN - 2; ++i ) {
10496 #ifdef DEBUG_MATCHING_NODES
10497           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10498 #endif
10499           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10500         }
10501       }
10502
10503       // add other links of the face 1 to linkList
10504       // -----------------------------------------
10505
10506       const SMDS_MeshElement* f0 = face[0];
10507       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10508       for ( int i = 0; i < nbN; i++ )
10509       {
10510         const SMDS_MeshNode* n2 = f0->GetNode( i );
10511         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10512           linkSet.insert( SMESH_TLink( n1, n2 ));
10513         if ( !iter_isnew.second ) { // already in a set: no need to process
10514           linkSet.erase( iter_isnew.first );
10515         }
10516         else // new in set == encountered for the first time: add
10517         {
10518 #ifdef DEBUG_MATCHING_NODES
10519           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10520                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10521 #endif
10522           linkList[0].push_back ( NLink( n1, n2 ));
10523           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10524         }
10525         n1 = n2;
10526       }
10527     } // 2 faces found
10528   } // loop on link lists
10529
10530   return SEW_OK;
10531 }
10532
10533 //================================================================================
10534 /*!
10535  * \brief Create elements equal (on same nodes) to given ones
10536  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10537  *              elements of the uppest dimension are duplicated.
10538  */
10539 //================================================================================
10540
10541 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10542 {
10543   ClearLastCreated();
10544   SMESHDS_Mesh* mesh = GetMeshDS();
10545
10546   // get an element type and an iterator over elements
10547
10548   SMDSAbs_ElementType type;
10549   SMDS_ElemIteratorPtr elemIt;
10550   vector< const SMDS_MeshElement* > allElems;
10551   if ( theElements.empty() )
10552   {
10553     if ( mesh->NbNodes() == 0 )
10554       return;
10555     // get most complex type
10556     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10557       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10558       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10559     };
10560     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10561       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10562       {
10563         type = types[i];
10564         break;
10565       }
10566     // put all elements in the vector <allElems>
10567     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10568     elemIt = mesh->elementsIterator( type );
10569     while ( elemIt->more() )
10570       allElems.push_back( elemIt->next());
10571     elemIt = elemSetIterator( allElems );
10572   }
10573   else
10574   {
10575     type = (*theElements.begin())->GetType();
10576     elemIt = elemSetIterator( theElements );
10577   }
10578
10579   // duplicate elements
10580
10581   ElemFeatures elemType;
10582
10583   vector< const SMDS_MeshNode* > nodes;
10584   while ( elemIt->more() )
10585   {
10586     const SMDS_MeshElement* elem = elemIt->next();
10587     if ( elem->GetType() != type )
10588       continue;
10589
10590     elemType.Init( elem, /*basicOnly=*/false );
10591     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10592
10593     AddElement( nodes, elemType );
10594   }
10595 }
10596
10597 //================================================================================
10598 /*!
10599   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10600   \param theElems - the list of elements (edges or faces) to be replicated
10601   The nodes for duplication could be found from these elements
10602   \param theNodesNot - list of nodes to NOT replicate
10603   \param theAffectedElems - the list of elements (cells and edges) to which the
10604   replicated nodes should be associated to.
10605   \return TRUE if operation has been completed successfully, FALSE otherwise
10606 */
10607 //================================================================================
10608
10609 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10610                                     const TIDSortedElemSet& theNodesNot,
10611                                     const TIDSortedElemSet& theAffectedElems )
10612 {
10613   myLastCreatedElems.Clear();
10614   myLastCreatedNodes.Clear();
10615
10616   if ( theElems.size() == 0 )
10617     return false;
10618
10619   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10620   if ( !aMeshDS )
10621     return false;
10622
10623   bool res = false;
10624   TNodeNodeMap anOldNodeToNewNode;
10625   // duplicate elements and nodes
10626   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10627   // replce nodes by duplications
10628   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10629   return res;
10630 }
10631
10632 //================================================================================
10633 /*!
10634   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10635   \param theMeshDS - mesh instance
10636   \param theElems - the elements replicated or modified (nodes should be changed)
10637   \param theNodesNot - nodes to NOT replicate
10638   \param theNodeNodeMap - relation of old node to new created node
10639   \param theIsDoubleElem - flag os to replicate element or modify
10640   \return TRUE if operation has been completed successfully, FALSE otherwise
10641 */
10642 //================================================================================
10643
10644 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10645                                    const TIDSortedElemSet& theElems,
10646                                    const TIDSortedElemSet& theNodesNot,
10647                                    TNodeNodeMap&           theNodeNodeMap,
10648                                    const bool              theIsDoubleElem )
10649 {
10650   MESSAGE("doubleNodes");
10651   // iterate through element and duplicate them (by nodes duplication)
10652   bool res = false;
10653   std::vector<const SMDS_MeshNode*> newNodes;
10654   ElemFeatures elemType;
10655
10656   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10657   for ( ;  elemItr != theElems.end(); ++elemItr )
10658   {
10659     const SMDS_MeshElement* anElem = *elemItr;
10660     if (!anElem)
10661       continue;
10662
10663     // duplicate nodes to duplicate element
10664     bool isDuplicate = false;
10665     newNodes.resize( anElem->NbNodes() );
10666     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10667     int ind = 0;
10668     while ( anIter->more() )
10669     {
10670       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10671       const SMDS_MeshNode*  aNewNode = aCurrNode;
10672       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10673       if ( n2n != theNodeNodeMap.end() )
10674       {
10675         aNewNode = n2n->second;
10676       }
10677       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10678       {
10679         // duplicate node
10680         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10681         copyPosition( aCurrNode, aNewNode );
10682         theNodeNodeMap[ aCurrNode ] = aNewNode;
10683         myLastCreatedNodes.Append( aNewNode );
10684       }
10685       isDuplicate |= (aCurrNode != aNewNode);
10686       newNodes[ ind++ ] = aNewNode;
10687     }
10688     if ( !isDuplicate )
10689       continue;
10690
10691     if ( theIsDoubleElem )
10692       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10693     else
10694       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10695
10696     res = true;
10697   }
10698   return res;
10699 }
10700
10701 //================================================================================
10702 /*!
10703   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704   \param theNodes - identifiers of nodes to be doubled
10705   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10706   nodes. If list of element identifiers is empty then nodes are doubled but
10707   they not assigned to elements
10708   \return TRUE if operation has been completed successfully, FALSE otherwise
10709 */
10710 //================================================================================
10711
10712 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10713                                     const std::list< int >& theListOfModifiedElems )
10714 {
10715   MESSAGE("DoubleNodes");
10716   myLastCreatedElems.Clear();
10717   myLastCreatedNodes.Clear();
10718
10719   if ( theListOfNodes.size() == 0 )
10720     return false;
10721
10722   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10723   if ( !aMeshDS )
10724     return false;
10725
10726   // iterate through nodes and duplicate them
10727
10728   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10729
10730   std::list< int >::const_iterator aNodeIter;
10731   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10732   {
10733     int aCurr = *aNodeIter;
10734     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10735     if ( !aNode )
10736       continue;
10737
10738     // duplicate node
10739
10740     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10741     if ( aNewNode )
10742     {
10743       copyPosition( aNode, aNewNode );
10744       anOldNodeToNewNode[ aNode ] = aNewNode;
10745       myLastCreatedNodes.Append( aNewNode );
10746     }
10747   }
10748
10749   // Create map of new nodes for modified elements
10750
10751   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10752
10753   std::list< int >::const_iterator anElemIter;
10754   for ( anElemIter = theListOfModifiedElems.begin();
10755         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10756   {
10757     int aCurr = *anElemIter;
10758     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10759     if ( !anElem )
10760       continue;
10761
10762     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10763
10764     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10765     int ind = 0;
10766     while ( anIter->more() )
10767     {
10768       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10769       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10770       {
10771         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10772         aNodeArr[ ind++ ] = aNewNode;
10773       }
10774       else
10775         aNodeArr[ ind++ ] = aCurrNode;
10776     }
10777     anElemToNodes[ anElem ] = aNodeArr;
10778   }
10779
10780   // Change nodes of elements
10781
10782   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10783     anElemToNodesIter = anElemToNodes.begin();
10784   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10785   {
10786     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10787     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10788     if ( anElem )
10789       {
10790       MESSAGE("ChangeElementNodes");
10791       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10792       }
10793   }
10794
10795   return true;
10796 }
10797
10798 namespace {
10799
10800   //================================================================================
10801   /*!
10802   \brief Check if element located inside shape
10803   \return TRUE if IN or ON shape, FALSE otherwise
10804   */
10805   //================================================================================
10806
10807   template<class Classifier>
10808   bool isInside(const SMDS_MeshElement* theElem,
10809                 Classifier&             theClassifier,
10810                 const double            theTol)
10811   {
10812     gp_XYZ centerXYZ (0, 0, 0);
10813     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10814     while (aNodeItr->more())
10815       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10816
10817     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10818     theClassifier.Perform(aPnt, theTol);
10819     TopAbs_State aState = theClassifier.State();
10820     return (aState == TopAbs_IN || aState == TopAbs_ON );
10821   }
10822
10823   //================================================================================
10824   /*!
10825    * \brief Classifier of the 3D point on the TopoDS_Face
10826    *        with interaface suitable for isInside()
10827    */
10828   //================================================================================
10829
10830   struct _FaceClassifier
10831   {
10832     Extrema_ExtPS       _extremum;
10833     BRepAdaptor_Surface _surface;
10834     TopAbs_State        _state;
10835
10836     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10837     {
10838       _extremum.Initialize( _surface,
10839                             _surface.FirstUParameter(), _surface.LastUParameter(),
10840                             _surface.FirstVParameter(), _surface.LastVParameter(),
10841                             _surface.Tolerance(), _surface.Tolerance() );
10842     }
10843     void Perform(const gp_Pnt& aPnt, double theTol)
10844     {
10845       theTol *= theTol;
10846       _state = TopAbs_OUT;
10847       _extremum.Perform(aPnt);
10848       if ( _extremum.IsDone() )
10849         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10850           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10851     }
10852     TopAbs_State State() const
10853     {
10854       return _state;
10855     }
10856   };
10857 }
10858
10859 //================================================================================
10860 /*!
10861   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10862   This method is the first step of DoubleNodeElemGroupsInRegion.
10863   \param theElems - list of groups of elements (edges or faces) to be replicated
10864   \param theNodesNot - list of groups of nodes not to replicated
10865   \param theShape - shape to detect affected elements (element which geometric center
10866          located on or inside shape). If the shape is null, detection is done on faces orientations
10867          (select elements with a gravity center on the side given by faces normals).
10868          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10869          The replicated nodes should be associated to affected elements.
10870   \return groups of affected elements
10871   \sa DoubleNodeElemGroupsInRegion()
10872  */
10873 //================================================================================
10874
10875 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10876                                                    const TIDSortedElemSet& theNodesNot,
10877                                                    const TopoDS_Shape&     theShape,
10878                                                    TIDSortedElemSet&       theAffectedElems)
10879 {
10880   if ( theShape.IsNull() )
10881   {
10882     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10883     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10884     std::set<const SMDS_MeshElement*> edgesToCheck;
10885     alreadyCheckedNodes.clear();
10886     alreadyCheckedElems.clear();
10887     edgesToCheck.clear();
10888
10889     // --- iterates on elements to be replicated and get elements by back references from their nodes
10890
10891     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10892     int ielem;
10893     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10894     {
10895       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10896       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10897         continue;
10898       gp_XYZ normal;
10899       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10900       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10901       std::set<const SMDS_MeshNode*> nodesElem;
10902       nodesElem.clear();
10903       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10904       while ( nodeItr->more() )
10905       {
10906         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10907         nodesElem.insert(aNode);
10908       }
10909       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10910       for (; nodit != nodesElem.end(); nodit++)
10911       {
10912         MESSAGE("  noeud ");
10913         const SMDS_MeshNode* aNode = *nodit;
10914         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10915           continue;
10916         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10917           continue;
10918         alreadyCheckedNodes.insert(aNode);
10919         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10920         while ( backElemItr->more() )
10921         {
10922           MESSAGE("    backelem ");
10923           const SMDS_MeshElement* curElem = backElemItr->next();
10924           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10925             continue;
10926           if (theElems.find(curElem) != theElems.end())
10927             continue;
10928           alreadyCheckedElems.insert(curElem);
10929           double x=0, y=0, z=0;
10930           int nb = 0;
10931           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10932           while ( nodeItr2->more() )
10933           {
10934             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10935             x += anotherNode->X();
10936             y += anotherNode->Y();
10937             z += anotherNode->Z();
10938             nb++;
10939           }
10940           gp_XYZ p;
10941           p.SetCoord( x/nb -aNode->X(),
10942                       y/nb -aNode->Y(),
10943                       z/nb -aNode->Z() );
10944           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10945           if (normal*p > 0)
10946           {
10947             MESSAGE("    --- inserted")
10948             theAffectedElems.insert( curElem );
10949           }
10950           else if (curElem->GetType() == SMDSAbs_Edge)
10951             edgesToCheck.insert(curElem);
10952         }
10953       }
10954     }
10955     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10956     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10957     for( ; eit != edgesToCheck.end(); eit++)
10958     {
10959       bool onside = true;
10960       const SMDS_MeshElement* anEdge = *eit;
10961       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10962       while ( nodeItr->more() )
10963       {
10964         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10965         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10966         {
10967           onside = false;
10968           break;
10969         }
10970       }
10971       if (onside)
10972       {
10973         MESSAGE("    --- edge onside inserted")
10974         theAffectedElems.insert(anEdge);
10975       }
10976     }
10977   }
10978   else
10979   {
10980     const double aTol = Precision::Confusion();
10981     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10982     auto_ptr<_FaceClassifier>              aFaceClassifier;
10983     if ( theShape.ShapeType() == TopAbs_SOLID )
10984     {
10985       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10986       bsc3d->PerformInfinitePoint(aTol);
10987     }
10988     else if (theShape.ShapeType() == TopAbs_FACE )
10989     {
10990       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10991     }
10992
10993     // iterates on indicated elements and get elements by back references from their nodes
10994     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10995     int ielem;
10996     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10997     {
10998       MESSAGE("element " << ielem++);
10999       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11000       if (!anElem)
11001         continue;
11002       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11003       while ( nodeItr->more() )
11004       {
11005         MESSAGE("  noeud ");
11006         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11007         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11008           continue;
11009         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11010         while ( backElemItr->more() )
11011         {
11012           MESSAGE("    backelem ");
11013           const SMDS_MeshElement* curElem = backElemItr->next();
11014           if ( curElem && theElems.find(curElem) == theElems.end() &&
11015               ( bsc3d.get() ?
11016                 isInside( curElem, *bsc3d, aTol ) :
11017                 isInside( curElem, *aFaceClassifier, aTol )))
11018             theAffectedElems.insert( curElem );
11019         }
11020       }
11021     }
11022   }
11023   return true;
11024 }
11025
11026 //================================================================================
11027 /*!
11028   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11029   \param theElems - group of of elements (edges or faces) to be replicated
11030   \param theNodesNot - group of nodes not to replicate
11031   \param theShape - shape to detect affected elements (element which geometric center
11032   located on or inside shape).
11033   The replicated nodes should be associated to affected elements.
11034   \return TRUE if operation has been completed successfully, FALSE otherwise
11035 */
11036 //================================================================================
11037
11038 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11039                                             const TIDSortedElemSet& theNodesNot,
11040                                             const TopoDS_Shape&     theShape )
11041 {
11042   if ( theShape.IsNull() )
11043     return false;
11044
11045   const double aTol = Precision::Confusion();
11046   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11047   auto_ptr<_FaceClassifier>              aFaceClassifier;
11048   if ( theShape.ShapeType() == TopAbs_SOLID )
11049   {
11050     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11051     bsc3d->PerformInfinitePoint(aTol);
11052   }
11053   else if (theShape.ShapeType() == TopAbs_FACE )
11054   {
11055     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11056   }
11057
11058   // iterates on indicated elements and get elements by back references from their nodes
11059   TIDSortedElemSet anAffected;
11060   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11061   for ( ;  elemItr != theElems.end(); ++elemItr )
11062   {
11063     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11064     if (!anElem)
11065       continue;
11066
11067     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11068     while ( nodeItr->more() )
11069     {
11070       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11071       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11072         continue;
11073       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11074       while ( backElemItr->more() )
11075       {
11076         const SMDS_MeshElement* curElem = backElemItr->next();
11077         if ( curElem && theElems.find(curElem) == theElems.end() &&
11078              ( bsc3d.get() ?
11079                isInside( curElem, *bsc3d, aTol ) :
11080                isInside( curElem, *aFaceClassifier, aTol )))
11081           anAffected.insert( curElem );
11082       }
11083     }
11084   }
11085   return DoubleNodes( theElems, theNodesNot, anAffected );
11086 }
11087
11088 /*!
11089  *  \brief compute an oriented angle between two planes defined by four points.
11090  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11091  *  @param p0 base of the rotation axe
11092  *  @param p1 extremity of the rotation axe
11093  *  @param g1 belongs to the first plane
11094  *  @param g2 belongs to the second plane
11095  */
11096 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11097 {
11098 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11099 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11100 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11101 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11102   gp_Vec vref(p0, p1);
11103   gp_Vec v1(p0, g1);
11104   gp_Vec v2(p0, g2);
11105   gp_Vec n1 = vref.Crossed(v1);
11106   gp_Vec n2 = vref.Crossed(v2);
11107   try {
11108     return n2.AngleWithRef(n1, vref);
11109   }
11110   catch ( Standard_Failure ) {
11111   }
11112   return Max( v1.Magnitude(), v2.Magnitude() );
11113 }
11114
11115 /*!
11116  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11117  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11118  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11119  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11120  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11121  * 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.
11122  * 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.
11123  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11124  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11125  * \param theElems - list of groups of volumes, where a group of volume is a set of
11126  *        SMDS_MeshElements sorted by Id.
11127  * \param createJointElems - if TRUE, create the elements
11128  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11129  *        the boundary between \a theDomains and the rest mesh
11130  * \return TRUE if operation has been completed successfully, FALSE otherwise
11131  */
11132 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11133                                                      bool                                 createJointElems,
11134                                                      bool                                 onAllBoundaries)
11135 {
11136   MESSAGE("----------------------------------------------");
11137   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11138   MESSAGE("----------------------------------------------");
11139
11140   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11141   meshDS->BuildDownWardConnectivity(true);
11142   CHRONO(50);
11143   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11144
11145   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11146   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11147   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11148
11149   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11150   std::map<int,int>celldom; // cell vtkId --> domain
11151   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11152   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11153   faceDomains.clear();
11154   celldom.clear();
11155   cellDomains.clear();
11156   nodeDomains.clear();
11157   std::map<int,int> emptyMap;
11158   std::set<int> emptySet;
11159   emptyMap.clear();
11160
11161   MESSAGE(".. Number of domains :"<<theElems.size());
11162
11163   TIDSortedElemSet theRestDomElems;
11164   const int iRestDom  = -1;
11165   const int idom0     = onAllBoundaries ? iRestDom : 0;
11166   const int nbDomains = theElems.size();
11167
11168   // Check if the domains do not share an element
11169   for (int idom = 0; idom < nbDomains-1; idom++)
11170     {
11171 //       MESSAGE("... Check of domain #" << idom);
11172       const TIDSortedElemSet& domain = theElems[idom];
11173       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11174       for (; elemItr != domain.end(); ++elemItr)
11175         {
11176           const SMDS_MeshElement* anElem = *elemItr;
11177           int idombisdeb = idom + 1 ;
11178           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11179           {
11180             const TIDSortedElemSet& domainbis = theElems[idombis];
11181             if ( domainbis.count(anElem) )
11182             {
11183               MESSAGE(".... Domain #" << idom);
11184               MESSAGE(".... Domain #" << idombis);
11185               throw SALOME_Exception("The domains are not disjoint.");
11186               return false ;
11187             }
11188           }
11189         }
11190     }
11191
11192   for (int idom = 0; idom < nbDomains; idom++)
11193     {
11194
11195       // --- build a map (face to duplicate --> volume to modify)
11196       //     with all the faces shared by 2 domains (group of elements)
11197       //     and corresponding volume of this domain, for each shared face.
11198       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11199
11200       MESSAGE("... Neighbors of domain #" << idom);
11201       const TIDSortedElemSet& domain = theElems[idom];
11202       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11203       for (; elemItr != domain.end(); ++elemItr)
11204         {
11205           const SMDS_MeshElement* anElem = *elemItr;
11206           if (!anElem)
11207             continue;
11208           int vtkId = anElem->getVtkId();
11209           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11210           int neighborsVtkIds[NBMAXNEIGHBORS];
11211           int downIds[NBMAXNEIGHBORS];
11212           unsigned char downTypes[NBMAXNEIGHBORS];
11213           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11214           for (int n = 0; n < nbNeighbors; n++)
11215             {
11216               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11217               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11218               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11219                 {
11220                   bool ok = false ;
11221                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11222                   {
11223                     // MESSAGE("Domain " << idombis);
11224                     const TIDSortedElemSet& domainbis = theElems[idombis];
11225                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11226                   }
11227                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11228                   {
11229                     DownIdType face(downIds[n], downTypes[n]);
11230                     if (!faceDomains[face].count(idom))
11231                       {
11232                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11233                         celldom[vtkId] = idom;
11234                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11235                       }
11236                     if ( !ok )
11237                     {
11238                       theRestDomElems.insert( elem );
11239                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11240                       celldom[neighborsVtkIds[n]] = iRestDom;
11241                     }
11242                   }
11243                 }
11244             }
11245         }
11246     }
11247
11248   //MESSAGE("Number of shared faces " << faceDomains.size());
11249   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11250
11251   // --- explore the shared faces domain by domain,
11252   //     explore the nodes of the face and see if they belong to a cell in the domain,
11253   //     which has only a node or an edge on the border (not a shared face)
11254
11255   for (int idomain = idom0; idomain < nbDomains; idomain++)
11256     {
11257       //MESSAGE("Domain " << idomain);
11258       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11259       itface = faceDomains.begin();
11260       for (; itface != faceDomains.end(); ++itface)
11261         {
11262           const std::map<int, int>& domvol = itface->second;
11263           if (!domvol.count(idomain))
11264             continue;
11265           DownIdType face = itface->first;
11266           //MESSAGE(" --- face " << face.cellId);
11267           std::set<int> oldNodes;
11268           oldNodes.clear();
11269           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11270           std::set<int>::iterator itn = oldNodes.begin();
11271           for (; itn != oldNodes.end(); ++itn)
11272             {
11273               int oldId = *itn;
11274               //MESSAGE("     node " << oldId);
11275               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11276               for (int i=0; i<l.ncells; i++)
11277                 {
11278                   int vtkId = l.cells[i];
11279                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11280                   if (!domain.count(anElem))
11281                     continue;
11282                   int vtkType = grid->GetCellType(vtkId);
11283                   int downId = grid->CellIdToDownId(vtkId);
11284                   if (downId < 0)
11285                     {
11286                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11287                       continue; // not OK at this stage of the algorithm:
11288                                 //no cells created after BuildDownWardConnectivity
11289                     }
11290                   DownIdType aCell(downId, vtkType);
11291                   cellDomains[aCell][idomain] = vtkId;
11292                   celldom[vtkId] = idomain;
11293                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11294                 }
11295             }
11296         }
11297     }
11298
11299   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11300   //     for each shared face, get the nodes
11301   //     for each node, for each domain of the face, create a clone of the node
11302
11303   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11304   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11305   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11306
11307   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11308   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11309   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11310
11311   MESSAGE(".. Duplication of the nodes");
11312   for (int idomain = idom0; idomain < nbDomains; idomain++)
11313     {
11314       itface = faceDomains.begin();
11315       for (; itface != faceDomains.end(); ++itface)
11316         {
11317           const std::map<int, int>& domvol = itface->second;
11318           if (!domvol.count(idomain))
11319             continue;
11320           DownIdType face = itface->first;
11321           //MESSAGE(" --- face " << face.cellId);
11322           std::set<int> oldNodes;
11323           oldNodes.clear();
11324           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11325           std::set<int>::iterator itn = oldNodes.begin();
11326           for (; itn != oldNodes.end(); ++itn)
11327             {
11328               int oldId = *itn;
11329               if (nodeDomains[oldId].empty())
11330                 {
11331                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11332                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11333                 }
11334               std::map<int, int>::const_iterator itdom = domvol.begin();
11335               for (; itdom != domvol.end(); ++itdom)
11336                 {
11337                   int idom = itdom->first;
11338                   //MESSAGE("         domain " << idom);
11339                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11340                     {
11341                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11342                         {
11343                           vector<int> orderedDoms;
11344                           //MESSAGE("multiple node " << oldId);
11345                           if (mutipleNodes.count(oldId))
11346                             orderedDoms = mutipleNodes[oldId];
11347                           else
11348                             {
11349                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11350                               for (; it != nodeDomains[oldId].end(); ++it)
11351                                 orderedDoms.push_back(it->first);
11352                             }
11353                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11354                           //stringstream txt;
11355                           //for (int i=0; i<orderedDoms.size(); i++)
11356                           //  txt << orderedDoms[i] << " ";
11357                           //MESSAGE("orderedDoms " << txt.str());
11358                           mutipleNodes[oldId] = orderedDoms;
11359                         }
11360                       double *coords = grid->GetPoint(oldId);
11361                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11362                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11363                       int newId = newNode->getVtkId();
11364                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11365                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11366                     }
11367                 }
11368             }
11369         }
11370     }
11371
11372   MESSAGE(".. Creation of elements");
11373   for (int idomain = idom0; idomain < nbDomains; idomain++)
11374     {
11375       itface = faceDomains.begin();
11376       for (; itface != faceDomains.end(); ++itface)
11377         {
11378           std::map<int, int> domvol = itface->second;
11379           if (!domvol.count(idomain))
11380             continue;
11381           DownIdType face = itface->first;
11382           //MESSAGE(" --- face " << face.cellId);
11383           std::set<int> oldNodes;
11384           oldNodes.clear();
11385           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11386           int nbMultipleNodes = 0;
11387           std::set<int>::iterator itn = oldNodes.begin();
11388           for (; itn != oldNodes.end(); ++itn)
11389             {
11390               int oldId = *itn;
11391               if (mutipleNodes.count(oldId))
11392                 nbMultipleNodes++;
11393             }
11394           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11395             {
11396               //MESSAGE("multiple Nodes detected on a shared face");
11397               int downId = itface->first.cellId;
11398               unsigned char cellType = itface->first.cellType;
11399               // --- shared edge or shared face ?
11400               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11401                 {
11402                   int nodes[3];
11403                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11404                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11405                     if (mutipleNodes.count(nodes[i]))
11406                       if (!mutipleNodesToFace.count(nodes[i]))
11407                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11408                 }
11409               else // shared face (between two volumes)
11410                 {
11411                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11412                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11413                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11414                   for (int ie =0; ie < nbEdges; ie++)
11415                     {
11416                       int nodes[3];
11417                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11418                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11419                         {
11420                           vector<int> vn0 = mutipleNodes[nodes[0]];
11421                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11422                           vector<int> doms;
11423                           for (int i0 = 0; i0 < vn0.size(); i0++)
11424                             for (int i1 = 0; i1 < vn1.size(); i1++)
11425                               if (vn0[i0] == vn1[i1])
11426                                 doms.push_back(vn0[i0]);
11427                           if (doms.size() >2)
11428                             {
11429                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11430                               double *coords = grid->GetPoint(nodes[0]);
11431                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11432                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11433                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11434                               gp_Pnt gref;
11435                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11436                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11437                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11438                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11439                               for (int id=0; id < doms.size(); id++)
11440                                 {
11441                                   int idom = doms[id];
11442                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11443                                   for (int ivol=0; ivol<nbvol; ivol++)
11444                                     {
11445                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11446                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11447                                       if (domain.count(elem))
11448                                         {
11449                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11450                                           domvol[idom] = svol;
11451                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11452                                           double values[3];
11453                                           vtkIdType npts = 0;
11454                                           vtkIdType* pts = 0;
11455                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11456                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11457                                           if (id ==0)
11458                                             {
11459                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11460                                               angleDom[idom] = 0;
11461                                             }
11462                                           else
11463                                             {
11464                                               gp_Pnt g(values[0], values[1], values[2]);
11465                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11466                                               //MESSAGE("  angle=" << angleDom[idom]);
11467                                             }
11468                                           break;
11469                                         }
11470                                     }
11471                                 }
11472                               map<double, int> sortedDom; // sort domains by angle
11473                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11474                                 sortedDom[ia->second] = ia->first;
11475                               vector<int> vnodes;
11476                               vector<int> vdom;
11477                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11478                                 {
11479                                   vdom.push_back(ib->second);
11480                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11481                                 }
11482                               for (int ino = 0; ino < nbNodes; ino++)
11483                                 vnodes.push_back(nodes[ino]);
11484                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11485                             }
11486                         }
11487                     }
11488                 }
11489             }
11490         }
11491     }
11492
11493   // --- iterate on shared faces (volumes to modify, face to extrude)
11494   //     get node id's of the face (id SMDS = id VTK)
11495   //     create flat element with old and new nodes if requested
11496
11497   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11498   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11499
11500   std::map<int, std::map<long,int> > nodeQuadDomains;
11501   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11502
11503   MESSAGE(".. Creation of elements: simple junction");
11504   if (createJointElems)
11505     {
11506       int idg;
11507       string joints2DName = "joints2D";
11508       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11509       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11510       string joints3DName = "joints3D";
11511       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11512       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11513
11514       itface = faceDomains.begin();
11515       for (; itface != faceDomains.end(); ++itface)
11516         {
11517           DownIdType face = itface->first;
11518           std::set<int> oldNodes;
11519           std::set<int>::iterator itn;
11520           oldNodes.clear();
11521           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11522
11523           std::map<int, int> domvol = itface->second;
11524           std::map<int, int>::iterator itdom = domvol.begin();
11525           int dom1 = itdom->first;
11526           int vtkVolId = itdom->second;
11527           itdom++;
11528           int dom2 = itdom->first;
11529           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11530                                                              nodeQuadDomains);
11531           stringstream grpname;
11532           grpname << "j_";
11533           if (dom1 < dom2)
11534             grpname << dom1 << "_" << dom2;
11535           else
11536             grpname << dom2 << "_" << dom1;
11537           string namegrp = grpname.str();
11538           if (!mapOfJunctionGroups.count(namegrp))
11539             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11540           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11541           if (sgrp)
11542             sgrp->Add(vol->GetID());
11543           if (vol->GetType() == SMDSAbs_Volume)
11544             joints3DGrp->Add(vol->GetID());
11545           else if (vol->GetType() == SMDSAbs_Face)
11546             joints2DGrp->Add(vol->GetID());
11547         }
11548     }
11549
11550   // --- create volumes on multiple domain intersection if requested
11551   //     iterate on mutipleNodesToFace
11552   //     iterate on edgesMultiDomains
11553
11554   MESSAGE(".. Creation of elements: multiple junction");
11555   if (createJointElems)
11556     {
11557       // --- iterate on mutipleNodesToFace
11558
11559       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11560       for (; itn != mutipleNodesToFace.end(); ++itn)
11561         {
11562           int node = itn->first;
11563           vector<int> orderDom = itn->second;
11564           vector<vtkIdType> orderedNodes;
11565           for (int idom = 0; idom <orderDom.size(); idom++)
11566             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11567             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11568
11569             stringstream grpname;
11570             grpname << "m2j_";
11571             grpname << 0 << "_" << 0;
11572             int idg;
11573             string namegrp = grpname.str();
11574             if (!mapOfJunctionGroups.count(namegrp))
11575               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11576             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11577             if (sgrp)
11578               sgrp->Add(face->GetID());
11579         }
11580
11581       // --- iterate on edgesMultiDomains
11582
11583       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11584       for (; ite != edgesMultiDomains.end(); ++ite)
11585         {
11586           vector<int> nodes = ite->first;
11587           vector<int> orderDom = ite->second;
11588           vector<vtkIdType> orderedNodes;
11589           if (nodes.size() == 2)
11590             {
11591               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11592               for (int ino=0; ino < nodes.size(); ino++)
11593                 if (orderDom.size() == 3)
11594                   for (int idom = 0; idom <orderDom.size(); idom++)
11595                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11596                 else
11597                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11598                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11599               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11600
11601               int idg;
11602               string namegrp = "jointsMultiples";
11603               if (!mapOfJunctionGroups.count(namegrp))
11604                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11605               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11606               if (sgrp)
11607                 sgrp->Add(vol->GetID());
11608             }
11609           else
11610             {
11611               INFOS("Quadratic multiple joints not implemented");
11612               // TODO quadratic nodes
11613             }
11614         }
11615     }
11616
11617   // --- list the explicit faces and edges of the mesh that need to be modified,
11618   //     i.e. faces and edges built with one or more duplicated nodes.
11619   //     associate these faces or edges to their corresponding domain.
11620   //     only the first domain found is kept when a face or edge is shared
11621
11622   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11623   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11624   faceOrEdgeDom.clear();
11625   feDom.clear();
11626
11627   MESSAGE(".. Modification of elements");
11628   for (int idomain = idom0; idomain < nbDomains; idomain++)
11629     {
11630       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11631       for (; itnod != nodeDomains.end(); ++itnod)
11632         {
11633           int oldId = itnod->first;
11634           //MESSAGE("     node " << oldId);
11635           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11636           for (int i = 0; i < l.ncells; i++)
11637             {
11638               int vtkId = l.cells[i];
11639               int vtkType = grid->GetCellType(vtkId);
11640               int downId = grid->CellIdToDownId(vtkId);
11641               if (downId < 0)
11642                 continue; // new cells: not to be modified
11643               DownIdType aCell(downId, vtkType);
11644               int volParents[1000];
11645               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11646               for (int j = 0; j < nbvol; j++)
11647                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11648                   if (!feDom.count(vtkId))
11649                     {
11650                       feDom[vtkId] = idomain;
11651                       faceOrEdgeDom[aCell] = emptyMap;
11652                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11653                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11654                       //        << " type " << vtkType << " downId " << downId);
11655                     }
11656             }
11657         }
11658     }
11659
11660   // --- iterate on shared faces (volumes to modify, face to extrude)
11661   //     get node id's of the face
11662   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11663
11664   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11665   for (int m=0; m<3; m++)
11666     {
11667       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11668       itface = (*amap).begin();
11669       for (; itface != (*amap).end(); ++itface)
11670         {
11671           DownIdType face = itface->first;
11672           std::set<int> oldNodes;
11673           std::set<int>::iterator itn;
11674           oldNodes.clear();
11675           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11676           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11677           std::map<int, int> localClonedNodeIds;
11678
11679           std::map<int, int> domvol = itface->second;
11680           std::map<int, int>::iterator itdom = domvol.begin();
11681           for (; itdom != domvol.end(); ++itdom)
11682             {
11683               int idom = itdom->first;
11684               int vtkVolId = itdom->second;
11685               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11686               localClonedNodeIds.clear();
11687               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11688                 {
11689                   int oldId = *itn;
11690                   if (nodeDomains[oldId].count(idom))
11691                     {
11692                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11693                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11694                     }
11695                 }
11696               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11697             }
11698         }
11699     }
11700
11701   // Remove empty groups (issue 0022812)
11702   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11703   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11704   {
11705     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11706       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11707   }
11708
11709   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11710   grid->BuildLinks();
11711
11712   CHRONOSTOP(50);
11713   counters::stats();
11714   return true;
11715 }
11716
11717 /*!
11718  * \brief Double nodes on some external faces and create flat elements.
11719  * Flat elements are mainly used by some types of mechanic calculations.
11720  *
11721  * Each group of the list must be constituted of faces.
11722  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11723  * @param theElems - list of groups of faces, where a group of faces is a set of
11724  * SMDS_MeshElements sorted by Id.
11725  * @return TRUE if operation has been completed successfully, FALSE otherwise
11726  */
11727 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11728 {
11729   MESSAGE("-------------------------------------------------");
11730   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11731   MESSAGE("-------------------------------------------------");
11732
11733   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11734
11735   // --- For each group of faces
11736   //     duplicate the nodes, create a flat element based on the face
11737   //     replace the nodes of the faces by their clones
11738
11739   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11740   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11741   clonedNodes.clear();
11742   intermediateNodes.clear();
11743   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11744   mapOfJunctionGroups.clear();
11745
11746   for (int idom = 0; idom < theElems.size(); idom++)
11747     {
11748       const TIDSortedElemSet& domain = theElems[idom];
11749       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11750       for (; elemItr != domain.end(); ++elemItr)
11751         {
11752           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11753           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11754           if (!aFace)
11755             continue;
11756           // MESSAGE("aFace=" << aFace->GetID());
11757           bool isQuad = aFace->IsQuadratic();
11758           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11759
11760           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11761
11762           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11763           while (nodeIt->more())
11764             {
11765               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11766               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11767               if (isMedium)
11768                 ln2.push_back(node);
11769               else
11770                 ln0.push_back(node);
11771
11772               const SMDS_MeshNode* clone = 0;
11773               if (!clonedNodes.count(node))
11774                 {
11775                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11776                   copyPosition( node, clone );
11777                   clonedNodes[node] = clone;
11778                 }
11779               else
11780                 clone = clonedNodes[node];
11781
11782               if (isMedium)
11783                 ln3.push_back(clone);
11784               else
11785                 ln1.push_back(clone);
11786
11787               const SMDS_MeshNode* inter = 0;
11788               if (isQuad && (!isMedium))
11789                 {
11790                   if (!intermediateNodes.count(node))
11791                     {
11792                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11793                       copyPosition( node, inter );
11794                       intermediateNodes[node] = inter;
11795                     }
11796                   else
11797                     inter = intermediateNodes[node];
11798                   ln4.push_back(inter);
11799                 }
11800             }
11801
11802           // --- extrude the face
11803
11804           vector<const SMDS_MeshNode*> ln;
11805           SMDS_MeshVolume* vol = 0;
11806           vtkIdType aType = aFace->GetVtkType();
11807           switch (aType)
11808           {
11809             case VTK_TRIANGLE:
11810               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11811               // MESSAGE("vol prism " << vol->GetID());
11812               ln.push_back(ln1[0]);
11813               ln.push_back(ln1[1]);
11814               ln.push_back(ln1[2]);
11815               break;
11816             case VTK_QUAD:
11817               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11818               // MESSAGE("vol hexa " << vol->GetID());
11819               ln.push_back(ln1[0]);
11820               ln.push_back(ln1[1]);
11821               ln.push_back(ln1[2]);
11822               ln.push_back(ln1[3]);
11823               break;
11824             case VTK_QUADRATIC_TRIANGLE:
11825               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11826                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11827               // MESSAGE("vol quad prism " << vol->GetID());
11828               ln.push_back(ln1[0]);
11829               ln.push_back(ln1[1]);
11830               ln.push_back(ln1[2]);
11831               ln.push_back(ln3[0]);
11832               ln.push_back(ln3[1]);
11833               ln.push_back(ln3[2]);
11834               break;
11835             case VTK_QUADRATIC_QUAD:
11836 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11837 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11838 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11839               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11840                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11841                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11842               // MESSAGE("vol quad hexa " << vol->GetID());
11843               ln.push_back(ln1[0]);
11844               ln.push_back(ln1[1]);
11845               ln.push_back(ln1[2]);
11846               ln.push_back(ln1[3]);
11847               ln.push_back(ln3[0]);
11848               ln.push_back(ln3[1]);
11849               ln.push_back(ln3[2]);
11850               ln.push_back(ln3[3]);
11851               break;
11852             case VTK_POLYGON:
11853               break;
11854             default:
11855               break;
11856           }
11857
11858           if (vol)
11859             {
11860               stringstream grpname;
11861               grpname << "jf_";
11862               grpname << idom;
11863               int idg;
11864               string namegrp = grpname.str();
11865               if (!mapOfJunctionGroups.count(namegrp))
11866                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11867               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11868               if (sgrp)
11869                 sgrp->Add(vol->GetID());
11870             }
11871
11872           // --- modify the face
11873
11874           aFace->ChangeNodes(&ln[0], ln.size());
11875         }
11876     }
11877   return true;
11878 }
11879
11880 /*!
11881  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11882  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11883  *  groups of faces to remove inside the object, (idem edges).
11884  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11885  */
11886 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11887                                       const TopoDS_Shape& theShape,
11888                                       SMESH_NodeSearcher* theNodeSearcher,
11889                                       const char* groupName,
11890                                       std::vector<double>&   nodesCoords,
11891                                       std::vector<std::vector<int> >& listOfListOfNodes)
11892 {
11893   MESSAGE("--------------------------------");
11894   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11895   MESSAGE("--------------------------------");
11896
11897   // --- zone of volumes to remove is given :
11898   //     1 either by a geom shape (one or more vertices) and a radius,
11899   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11900   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11901   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11902   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11903   //     defined by it's name.
11904
11905   SMESHDS_GroupBase* groupDS = 0;
11906   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11907   while ( groupIt->more() )
11908     {
11909       groupDS = 0;
11910       SMESH_Group * group = groupIt->next();
11911       if ( !group ) continue;
11912       groupDS = group->GetGroupDS();
11913       if ( !groupDS || groupDS->IsEmpty() ) continue;
11914       std::string grpName = group->GetName();
11915       //MESSAGE("grpName=" << grpName);
11916       if (grpName == groupName)
11917         break;
11918       else
11919         groupDS = 0;
11920     }
11921
11922   bool isNodeGroup = false;
11923   bool isNodeCoords = false;
11924   if (groupDS)
11925     {
11926       if (groupDS->GetType() != SMDSAbs_Node)
11927         return;
11928       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11929     }
11930
11931   if (nodesCoords.size() > 0)
11932     isNodeCoords = true; // a list o nodes given by their coordinates
11933   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11934
11935   // --- define groups to build
11936
11937   int idg; // --- group of SMDS volumes
11938   string grpvName = groupName;
11939   grpvName += "_vol";
11940   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11941   if (!grp)
11942     {
11943       MESSAGE("group not created " << grpvName);
11944       return;
11945     }
11946   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11947
11948   int idgs; // --- group of SMDS faces on the skin
11949   string grpsName = groupName;
11950   grpsName += "_skin";
11951   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11952   if (!grps)
11953     {
11954       MESSAGE("group not created " << grpsName);
11955       return;
11956     }
11957   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11958
11959   int idgi; // --- group of SMDS faces internal (several shapes)
11960   string grpiName = groupName;
11961   grpiName += "_internalFaces";
11962   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11963   if (!grpi)
11964     {
11965       MESSAGE("group not created " << grpiName);
11966       return;
11967     }
11968   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11969
11970   int idgei; // --- group of SMDS faces internal (several shapes)
11971   string grpeiName = groupName;
11972   grpeiName += "_internalEdges";
11973   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11974   if (!grpei)
11975     {
11976       MESSAGE("group not created " << grpeiName);
11977       return;
11978     }
11979   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11980
11981   // --- build downward connectivity
11982
11983   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11984   meshDS->BuildDownWardConnectivity(true);
11985   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11986
11987   // --- set of volumes detected inside
11988
11989   std::set<int> setOfInsideVol;
11990   std::set<int> setOfVolToCheck;
11991
11992   std::vector<gp_Pnt> gpnts;
11993   gpnts.clear();
11994
11995   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11996     {
11997       MESSAGE("group of nodes provided");
11998       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11999       while ( elemIt->more() )
12000         {
12001           const SMDS_MeshElement* elem = elemIt->next();
12002           if (!elem)
12003             continue;
12004           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12005           if (!node)
12006             continue;
12007           SMDS_MeshElement* vol = 0;
12008           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12009           while (volItr->more())
12010             {
12011               vol = (SMDS_MeshElement*)volItr->next();
12012               setOfInsideVol.insert(vol->getVtkId());
12013               sgrp->Add(vol->GetID());
12014             }
12015         }
12016     }
12017   else if (isNodeCoords)
12018     {
12019       MESSAGE("list of nodes coordinates provided");
12020       int i = 0;
12021       int k = 0;
12022       while (i < nodesCoords.size()-2)
12023         {
12024           double x = nodesCoords[i++];
12025           double y = nodesCoords[i++];
12026           double z = nodesCoords[i++];
12027           gp_Pnt p = gp_Pnt(x, y ,z);
12028           gpnts.push_back(p);
12029           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12030           k++;
12031         }
12032     }
12033   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12034     {
12035       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12036       TopTools_IndexedMapOfShape vertexMap;
12037       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12038       gp_Pnt p = gp_Pnt(0,0,0);
12039       if (vertexMap.Extent() < 1)
12040         return;
12041
12042       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12043         {
12044           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12045           p = BRep_Tool::Pnt(vertex);
12046           gpnts.push_back(p);
12047           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12048         }
12049     }
12050
12051   if (gpnts.size() > 0)
12052     {
12053       int nodeId = 0;
12054       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12055       if (startNode)
12056         nodeId = startNode->GetID();
12057       MESSAGE("nodeId " << nodeId);
12058
12059       double radius2 = radius*radius;
12060       MESSAGE("radius2 " << radius2);
12061
12062       // --- volumes on start node
12063
12064       setOfVolToCheck.clear();
12065       SMDS_MeshElement* startVol = 0;
12066       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12067       while (volItr->more())
12068         {
12069           startVol = (SMDS_MeshElement*)volItr->next();
12070           setOfVolToCheck.insert(startVol->getVtkId());
12071         }
12072       if (setOfVolToCheck.empty())
12073         {
12074           MESSAGE("No volumes found");
12075           return;
12076         }
12077
12078       // --- starting with central volumes then their neighbors, check if they are inside
12079       //     or outside the domain, until no more new neighbor volume is inside.
12080       //     Fill the group of inside volumes
12081
12082       std::map<int, double> mapOfNodeDistance2;
12083       mapOfNodeDistance2.clear();
12084       std::set<int> setOfOutsideVol;
12085       while (!setOfVolToCheck.empty())
12086         {
12087           std::set<int>::iterator it = setOfVolToCheck.begin();
12088           int vtkId = *it;
12089           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12090           bool volInside = false;
12091           vtkIdType npts = 0;
12092           vtkIdType* pts = 0;
12093           grid->GetCellPoints(vtkId, npts, pts);
12094           for (int i=0; i<npts; i++)
12095             {
12096               double distance2 = 0;
12097               if (mapOfNodeDistance2.count(pts[i]))
12098                 {
12099                   distance2 = mapOfNodeDistance2[pts[i]];
12100                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12101                 }
12102               else
12103                 {
12104                   double *coords = grid->GetPoint(pts[i]);
12105                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12106                   distance2 = 1.E40;
12107                   for (int j=0; j<gpnts.size(); j++)
12108                     {
12109                       double d2 = aPoint.SquareDistance(gpnts[j]);
12110                       if (d2 < distance2)
12111                         {
12112                           distance2 = d2;
12113                           if (distance2 < radius2)
12114                             break;
12115                         }
12116                     }
12117                   mapOfNodeDistance2[pts[i]] = distance2;
12118                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12119                 }
12120               if (distance2 < radius2)
12121                 {
12122                   volInside = true; // one or more nodes inside the domain
12123                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12124                   break;
12125                 }
12126             }
12127           if (volInside)
12128             {
12129               setOfInsideVol.insert(vtkId);
12130               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12131               int neighborsVtkIds[NBMAXNEIGHBORS];
12132               int downIds[NBMAXNEIGHBORS];
12133               unsigned char downTypes[NBMAXNEIGHBORS];
12134               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12135               for (int n = 0; n < nbNeighbors; n++)
12136                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12137                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12138             }
12139           else
12140             {
12141               setOfOutsideVol.insert(vtkId);
12142               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12143             }
12144           setOfVolToCheck.erase(vtkId);
12145         }
12146     }
12147
12148   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12149   //     If yes, add the volume to the inside set
12150
12151   bool addedInside = true;
12152   std::set<int> setOfVolToReCheck;
12153   while (addedInside)
12154     {
12155       MESSAGE(" --------------------------- re check");
12156       addedInside = false;
12157       std::set<int>::iterator itv = setOfInsideVol.begin();
12158       for (; itv != setOfInsideVol.end(); ++itv)
12159         {
12160           int vtkId = *itv;
12161           int neighborsVtkIds[NBMAXNEIGHBORS];
12162           int downIds[NBMAXNEIGHBORS];
12163           unsigned char downTypes[NBMAXNEIGHBORS];
12164           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12165           for (int n = 0; n < nbNeighbors; n++)
12166             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12167               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12168         }
12169       setOfVolToCheck = setOfVolToReCheck;
12170       setOfVolToReCheck.clear();
12171       while  (!setOfVolToCheck.empty())
12172         {
12173           std::set<int>::iterator it = setOfVolToCheck.begin();
12174           int vtkId = *it;
12175           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12176             {
12177               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12178               int countInside = 0;
12179               int neighborsVtkIds[NBMAXNEIGHBORS];
12180               int downIds[NBMAXNEIGHBORS];
12181               unsigned char downTypes[NBMAXNEIGHBORS];
12182               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12183               for (int n = 0; n < nbNeighbors; n++)
12184                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12185                   countInside++;
12186               MESSAGE("countInside " << countInside);
12187               if (countInside > 1)
12188                 {
12189                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12190                   setOfInsideVol.insert(vtkId);
12191                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12192                   addedInside = true;
12193                 }
12194               else
12195                 setOfVolToReCheck.insert(vtkId);
12196             }
12197           setOfVolToCheck.erase(vtkId);
12198         }
12199     }
12200
12201   // --- map of Downward faces at the boundary, inside the global volume
12202   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12203   //     fill group of SMDS faces inside the volume (when several volume shapes)
12204   //     fill group of SMDS faces on the skin of the global volume (if skin)
12205
12206   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12207   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12208   std::set<int>::iterator it = setOfInsideVol.begin();
12209   for (; it != setOfInsideVol.end(); ++it)
12210     {
12211       int vtkId = *it;
12212       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12213       int neighborsVtkIds[NBMAXNEIGHBORS];
12214       int downIds[NBMAXNEIGHBORS];
12215       unsigned char downTypes[NBMAXNEIGHBORS];
12216       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12217       for (int n = 0; n < nbNeighbors; n++)
12218         {
12219           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12220           if (neighborDim == 3)
12221             {
12222               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12223                 {
12224                   DownIdType face(downIds[n], downTypes[n]);
12225                   boundaryFaces[face] = vtkId;
12226                 }
12227               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12228               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12229               if (vtkFaceId >= 0)
12230                 {
12231                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12232                   // find also the smds edges on this face
12233                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12234                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12235                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12236                   for (int i = 0; i < nbEdges; i++)
12237                     {
12238                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12239                       if (vtkEdgeId >= 0)
12240                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12241                     }
12242                 }
12243             }
12244           else if (neighborDim == 2) // skin of the volume
12245             {
12246               DownIdType face(downIds[n], downTypes[n]);
12247               skinFaces[face] = vtkId;
12248               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12249               if (vtkFaceId >= 0)
12250                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12251             }
12252         }
12253     }
12254
12255   // --- identify the edges constituting the wire of each subshape on the skin
12256   //     define polylines with the nodes of edges, equivalent to wires
12257   //     project polylines on subshapes, and partition, to get geom faces
12258
12259   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12260   std::set<int> emptySet;
12261   emptySet.clear();
12262   std::set<int> shapeIds;
12263
12264   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12265   while (itelem->more())
12266     {
12267       const SMDS_MeshElement *elem = itelem->next();
12268       int shapeId = elem->getshapeId();
12269       int vtkId = elem->getVtkId();
12270       if (!shapeIdToVtkIdSet.count(shapeId))
12271         {
12272           shapeIdToVtkIdSet[shapeId] = emptySet;
12273           shapeIds.insert(shapeId);
12274         }
12275       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12276     }
12277
12278   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12279   std::set<DownIdType, DownIdCompare> emptyEdges;
12280   emptyEdges.clear();
12281
12282   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12283   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12284     {
12285       int shapeId = itShape->first;
12286       MESSAGE(" --- Shape ID --- "<< shapeId);
12287       shapeIdToEdges[shapeId] = emptyEdges;
12288
12289       std::vector<int> nodesEdges;
12290
12291       std::set<int>::iterator its = itShape->second.begin();
12292       for (; its != itShape->second.end(); ++its)
12293         {
12294           int vtkId = *its;
12295           MESSAGE("     " << vtkId);
12296           int neighborsVtkIds[NBMAXNEIGHBORS];
12297           int downIds[NBMAXNEIGHBORS];
12298           unsigned char downTypes[NBMAXNEIGHBORS];
12299           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12300           for (int n = 0; n < nbNeighbors; n++)
12301             {
12302               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12303                 continue;
12304               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12305               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12306               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12307                 {
12308                   DownIdType edge(downIds[n], downTypes[n]);
12309                   if (!shapeIdToEdges[shapeId].count(edge))
12310                     {
12311                       shapeIdToEdges[shapeId].insert(edge);
12312                       int vtkNodeId[3];
12313                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12314                       nodesEdges.push_back(vtkNodeId[0]);
12315                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12316                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12317                     }
12318                 }
12319             }
12320         }
12321
12322       std::list<int> order;
12323       order.clear();
12324       if (nodesEdges.size() > 0)
12325         {
12326           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12327           nodesEdges[0] = -1;
12328           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12329           nodesEdges[1] = -1; // do not reuse this edge
12330           bool found = true;
12331           while (found)
12332             {
12333               int nodeTofind = order.back(); // try first to push back
12334               int i = 0;
12335               for (i = 0; i<nodesEdges.size(); i++)
12336                 if (nodesEdges[i] == nodeTofind)
12337                   break;
12338               if (i == nodesEdges.size())
12339                 found = false; // no follower found on back
12340               else
12341                 {
12342                   if (i%2) // odd ==> use the previous one
12343                     if (nodesEdges[i-1] < 0)
12344                       found = false;
12345                     else
12346                       {
12347                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12348                         nodesEdges[i-1] = -1;
12349                       }
12350                   else // even ==> use the next one
12351                     if (nodesEdges[i+1] < 0)
12352                       found = false;
12353                     else
12354                       {
12355                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12356                         nodesEdges[i+1] = -1;
12357                       }
12358                 }
12359               if (found)
12360                 continue;
12361               // try to push front
12362               found = true;
12363               nodeTofind = order.front(); // try to push front
12364               for (i = 0; i<nodesEdges.size(); i++)
12365                 if (nodesEdges[i] == nodeTofind)
12366                   break;
12367               if (i == nodesEdges.size())
12368                 {
12369                   found = false; // no predecessor found on front
12370                   continue;
12371                 }
12372               if (i%2) // odd ==> use the previous one
12373                 if (nodesEdges[i-1] < 0)
12374                   found = false;
12375                 else
12376                   {
12377                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12378                     nodesEdges[i-1] = -1;
12379                   }
12380               else // even ==> use the next one
12381                 if (nodesEdges[i+1] < 0)
12382                   found = false;
12383                 else
12384                   {
12385                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12386                     nodesEdges[i+1] = -1;
12387                   }
12388             }
12389         }
12390
12391
12392       std::vector<int> nodes;
12393       nodes.push_back(shapeId);
12394       std::list<int>::iterator itl = order.begin();
12395       for (; itl != order.end(); itl++)
12396         {
12397           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12398           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12399         }
12400       listOfListOfNodes.push_back(nodes);
12401     }
12402
12403   //     partition geom faces with blocFissure
12404   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12405   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12406
12407   return;
12408 }
12409
12410
12411 //================================================================================
12412 /*!
12413  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12414  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12415  * \return TRUE if operation has been completed successfully, FALSE otherwise
12416  */
12417 //================================================================================
12418
12419 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12420 {
12421   // iterates on volume elements and detect all free faces on them
12422   SMESHDS_Mesh* aMesh = GetMeshDS();
12423   if (!aMesh)
12424     return false;
12425
12426   ElemFeatures faceType( SMDSAbs_Face );
12427   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12428   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12429   while(vIt->more())
12430   {
12431     const SMDS_MeshVolume* volume = vIt->next();
12432     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12433     vTool.SetExternalNormal();
12434     const int iQuad = volume->IsQuadratic();
12435     faceType.SetQuad( iQuad );
12436     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12437     {
12438       if (!vTool.IsFreeFace(iface))
12439         continue;
12440       nbFree++;
12441       vector<const SMDS_MeshNode *> nodes;
12442       int nbFaceNodes = vTool.NbFaceNodes(iface);
12443       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12444       int inode = 0;
12445       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12446         nodes.push_back(faceNodes[inode]);
12447
12448       if (iQuad) // add medium nodes
12449       {
12450         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12451           nodes.push_back(faceNodes[inode]);
12452         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12453           nodes.push_back(faceNodes[8]);
12454       }
12455       // add new face based on volume nodes
12456       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12457       {
12458         nbExisted++; // face already exsist
12459       }
12460       else
12461       {
12462         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12463         nbCreated++;
12464       }
12465     }
12466   }
12467   return ( nbFree == ( nbExisted + nbCreated ));
12468 }
12469
12470 namespace
12471 {
12472   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12473   {
12474     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12475       return n;
12476     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12477   }
12478 }
12479 //================================================================================
12480 /*!
12481  * \brief Creates missing boundary elements
12482  *  \param elements - elements whose boundary is to be checked
12483  *  \param dimension - defines type of boundary elements to create
12484  *  \param group - a group to store created boundary elements in
12485  *  \param targetMesh - a mesh to store created boundary elements in
12486  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12487  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12488  *                                boundary elements will be copied into the targetMesh
12489  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12490  *                                boundary elements will be added into the new group
12491  *  \param aroundElements - if true, elements will be created on boundary of given
12492  *                          elements else, on boundary of the whole mesh.
12493  * \return nb of added boundary elements
12494  */
12495 //================================================================================
12496
12497 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12498                                        Bnd_Dimension           dimension,
12499                                        SMESH_Group*            group/*=0*/,
12500                                        SMESH_Mesh*             targetMesh/*=0*/,
12501                                        bool                    toCopyElements/*=false*/,
12502                                        bool                    toCopyExistingBoundary/*=false*/,
12503                                        bool                    toAddExistingBondary/*= false*/,
12504                                        bool                    aroundElements/*= false*/)
12505 {
12506   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12507   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12508   // hope that all elements are of the same type, do not check them all
12509   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12510     throw SALOME_Exception(LOCALIZED("wrong element type"));
12511
12512   if ( !targetMesh )
12513     toCopyElements = toCopyExistingBoundary = false;
12514
12515   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12516   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12517   int nbAddedBnd = 0;
12518
12519   // editor adding present bnd elements and optionally holding elements to add to the group
12520   SMESH_MeshEditor* presentEditor;
12521   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12522   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12523
12524   SMESH_MesherHelper helper( *myMesh );
12525   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12526   SMDS_VolumeTool vTool;
12527   TIDSortedElemSet avoidSet;
12528   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12529   size_t inode;
12530
12531   typedef vector<const SMDS_MeshNode*> TConnectivity;
12532   TConnectivity tgtNodes;
12533   ElemFeatures elemKind( missType ), elemToCopy;
12534
12535   SMDS_ElemIteratorPtr eIt;
12536   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12537   else                  eIt = elemSetIterator( elements );
12538
12539   while (eIt->more())
12540   {
12541     const SMDS_MeshElement* elem = eIt->next();
12542     const int              iQuad = elem->IsQuadratic();
12543     elemKind.SetQuad( iQuad );
12544
12545     // ------------------------------------------------------------------------------------
12546     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12547     // ------------------------------------------------------------------------------------
12548     vector<const SMDS_MeshElement*> presentBndElems;
12549     vector<TConnectivity>           missingBndElems;
12550     TConnectivity nodes, elemNodes;
12551     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12552     {
12553       vTool.SetExternalNormal();
12554       const SMDS_MeshElement* otherVol = 0;
12555       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12556       {
12557         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12558              ( !aroundElements || elements.count( otherVol )))
12559           continue;
12560         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12561         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12562         if ( missType == SMDSAbs_Edge ) // boundary edges
12563         {
12564           nodes.resize( 2+iQuad );
12565           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12566           {
12567             for ( int j = 0; j < nodes.size(); ++j )
12568               nodes[j] = nn[ i+j ];
12569             if ( const SMDS_MeshElement* edge =
12570                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12571               presentBndElems.push_back( edge );
12572             else
12573               missingBndElems.push_back( nodes );
12574           }
12575         }
12576         else // boundary face
12577         {
12578           nodes.clear();
12579           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12580             nodes.push_back( nn[inode] ); // add corner nodes
12581           if (iQuad)
12582             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12583               nodes.push_back( nn[inode] ); // add medium nodes
12584           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12585           if ( iCenter > 0 )
12586             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12587
12588           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12589                                                                SMDSAbs_Face, /*noMedium=*/false ))
12590             presentBndElems.push_back( f );
12591           else
12592             missingBndElems.push_back( nodes );
12593
12594           if ( targetMesh != myMesh )
12595           {
12596             // add 1D elements on face boundary to be added to a new mesh
12597             const SMDS_MeshElement* edge;
12598             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12599             {
12600               if ( iQuad )
12601                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12602               else
12603                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12604               if ( edge && avoidSet.insert( edge ).second )
12605                 presentBndElems.push_back( edge );
12606             }
12607           }
12608         }
12609       }
12610     }
12611     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12612     {
12613       avoidSet.clear(), avoidSet.insert( elem );
12614       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12615                         SMDS_MeshElement::iterator() );
12616       elemNodes.push_back( elemNodes[0] );
12617       nodes.resize( 2 + iQuad );
12618       const int nbLinks = elem->NbCornerNodes();
12619       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12620       {
12621         nodes[0] = elemNodes[iN];
12622         nodes[1] = elemNodes[iN+1+iQuad];
12623         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12624           continue; // not free link
12625
12626         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12627         if ( const SMDS_MeshElement* edge =
12628              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12629           presentBndElems.push_back( edge );
12630         else
12631           missingBndElems.push_back( nodes );
12632       }
12633     }
12634
12635     // ---------------------------------
12636     // 2. Add missing boundary elements
12637     // ---------------------------------
12638     if ( targetMesh != myMesh )
12639       // instead of making a map of nodes in this mesh and targetMesh,
12640       // we create nodes with same IDs.
12641       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12642       {
12643         TConnectivity& srcNodes = missingBndElems[i];
12644         tgtNodes.resize( srcNodes.size() );
12645         for ( inode = 0; inode < srcNodes.size(); ++inode )
12646           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12647         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12648                                                                    missType,
12649                                                                    /*noMedium=*/false))
12650           continue;
12651         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12652         ++nbAddedBnd;
12653       }
12654     else
12655       for ( int i = 0; i < missingBndElems.size(); ++i )
12656       {
12657         TConnectivity& nodes = missingBndElems[i];
12658         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12659                                                                    missType,
12660                                                                    /*noMedium=*/false))
12661           continue;
12662         SMDS_MeshElement* newElem = 
12663           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12664         nbAddedBnd += bool( newElem );
12665
12666         // try to set a new element to a shape
12667         if ( myMesh->HasShapeToMesh() )
12668         {
12669           bool ok = true;
12670           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12671           const size_t nbN = nodes.size() / (iQuad+1 );
12672           for ( inode = 0; inode < nbN && ok; ++inode )
12673           {
12674             pair<int, TopAbs_ShapeEnum> i_stype =
12675               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12676             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12677               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12678           }
12679           if ( ok && mediumShapes.size() > 1 )
12680           {
12681             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12682             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12683             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12684             {
12685               if (( ok = ( stype_i->first != stype_i_0.first )))
12686                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12687                                         aMesh->IndexToShape( stype_i_0.second ));
12688             }
12689           }
12690           if ( ok && mediumShapes.begin()->first == missShapeType )
12691             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12692         }
12693       }
12694
12695     // ----------------------------------
12696     // 3. Copy present boundary elements
12697     // ----------------------------------
12698     if ( toCopyExistingBoundary )
12699       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12700       {
12701         const SMDS_MeshElement* e = presentBndElems[i];
12702         tgtNodes.resize( e->NbNodes() );
12703         for ( inode = 0; inode < nodes.size(); ++inode )
12704           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12705         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12706       }
12707     else // store present elements to add them to a group
12708       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12709       {
12710         presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12711       }
12712
12713   } // loop on given elements
12714
12715   // ---------------------------------------------
12716   // 4. Fill group with boundary elements
12717   // ---------------------------------------------
12718   if ( group )
12719   {
12720     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12721       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12722         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12723   }
12724   tgtEditor.myLastCreatedElems.Clear();
12725   tgtEditor2.myLastCreatedElems.Clear();
12726
12727   // -----------------------
12728   // 5. Copy given elements
12729   // -----------------------
12730   if ( toCopyElements && targetMesh != myMesh )
12731   {
12732     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12733     else                  eIt = elemSetIterator( elements );
12734     while (eIt->more())
12735     {
12736       const SMDS_MeshElement* elem = eIt->next();
12737       tgtNodes.resize( elem->NbNodes() );
12738       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12739         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12740       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12741
12742       tgtEditor.myLastCreatedElems.Clear();
12743     }
12744   }
12745   return nbAddedBnd;
12746 }
12747
12748 //================================================================================
12749 /*!
12750  * \brief Copy node position and set \a to node on the same geometry
12751  */
12752 //================================================================================
12753
12754 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12755                                      const SMDS_MeshNode* to )
12756 {
12757   if ( !from || !to ) return;
12758
12759   SMDS_PositionPtr pos = from->GetPosition();
12760   if ( !pos || from->getshapeId() < 1 ) return;
12761
12762   switch ( pos->GetTypeOfPosition() )
12763   {
12764   case SMDS_TOP_3DSPACE: break;
12765
12766   case SMDS_TOP_FACE:
12767   {
12768     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12769     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12770                                 fPos->GetUParameter(), fPos->GetVParameter() );
12771     break;
12772   }
12773   case SMDS_TOP_EDGE:
12774   {
12775     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12776     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12777     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12778     break;
12779   }
12780   case SMDS_TOP_VERTEX:
12781   {
12782     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12783     break;
12784   }
12785   case SMDS_TOP_UNSPEC:
12786   default:;
12787   }
12788 }