Salome HOME
Update of CheckDone
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2021  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_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #include <smIdType.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 //=======================================================================
111 //function : SMESH_MeshEditor
112 //purpose  :
113 //=======================================================================
114
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116   :myMesh( theMesh ) // theMesh may be NULL
117 {
118 }
119
120 //================================================================================
121 /*!
122  * \brief Return mesh DS
123  */
124 //================================================================================
125
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 {
128   return myMesh->GetMeshDS();
129 }
130
131
132 //================================================================================
133 /*!
134  * \brief Clears myLastCreatedNodes and myLastCreatedElems
135  */
136 //================================================================================
137
138 void SMESH_MeshEditor::ClearLastCreated()
139 {
140   SMESHUtils::FreeVector( myLastCreatedElems );
141   SMESHUtils::FreeVector( myLastCreatedNodes );
142 }
143
144 //================================================================================
145 /*!
146  * \brief Initializes members by an existing element
147  *  \param [in] elem - the source element
148  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149  */
150 //================================================================================
151
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
154 {
155   if ( elem )
156   {
157     myType = elem->GetType();
158     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159     {
160       myIsPoly = elem->IsPoly();
161       if ( myIsPoly )
162       {
163         myIsQuad = elem->IsQuadratic();
164         if ( myType == SMDSAbs_Volume && !basicOnly )
165         {
166           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const smIdType ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],node[13],node[14],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12],node[13],node[14] );
300       }
301       else if (nbnode == 18) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],
306                                                  node[15],node[16],node[17],ID );
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],
311                                                  node[15],node[16],node[17] );
312       }
313       else if (nbnode == 20) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19] );
324       }
325       else if (nbnode == 27) {
326         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327                                                  node[4], node[5], node[6], node[7],
328                                                  node[8], node[9], node[10],node[11],
329                                                  node[12],node[13],node[14],node[15],
330                                                  node[16],node[17],node[18],node[19],
331                                                  node[20],node[21],node[22],node[23],
332                                                  node[24],node[25],node[26], ID);
333         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
334                                                  node[4], node[5], node[6], node[7],
335                                                  node[8], node[9], node[10],node[11],
336                                                  node[12],node[13],node[14],node[15],
337                                                  node[16],node[17],node[18],node[19],
338                                                  node[20],node[21],node[22],node[23],
339                                                  node[24],node[25],node[26] );
340       }
341     }
342     else if ( !features.myIsQuad )
343     {
344       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
346     }
347     else
348     {
349       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
351     }
352     break;
353
354   case SMDSAbs_Edge:
355     if ( nbnode == 2 ) {
356       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357       else           e = mesh->AddEdge      (node[0], node[1] );
358     }
359     else if ( nbnode == 3 ) {
360       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
362     }
363     break;
364
365   case SMDSAbs_0DElement:
366     if ( nbnode == 1 ) {
367       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368       else           e = mesh->Add0DElement      (node[0] );
369     }
370     break;
371
372   case SMDSAbs_Node:
373     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
375     break;
376
377   case SMDSAbs_Ball:
378     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
380     break;
381
382   default:;
383   }
384   if ( e ) myLastCreatedElems.push_back( e );
385   return e;
386 }
387
388 //=======================================================================
389 /*!
390  * \brief Add element
391  */
392 //=======================================================================
393
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395                                                const ElemFeatures&      features)
396 {
397   vector<const SMDS_MeshNode*> nodes;
398   nodes.reserve( nodeIDs.size() );
399   vector<smIdType>::const_iterator id = nodeIDs.begin();
400   while ( id != nodeIDs.end() ) {
401     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402       nodes.push_back( node );
403     else
404       return 0;
405   }
406   return AddElement( nodes, features );
407 }
408
409 //=======================================================================
410 //function : Remove
411 //purpose  : Remove a node or an element.
412 //           Modify a compute state of sub-meshes which become empty
413 //=======================================================================
414
415 smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
416                               const bool         isNodes )
417 {
418   ClearLastCreated();
419
420   SMESHDS_Mesh* aMesh = GetMeshDS();
421   set< SMESH_subMesh *> smmap;
422
423   smIdType removed = 0;
424   list<smIdType>::const_iterator it = theIDs.begin();
425   for ( ; it != theIDs.end(); it++ ) {
426     const SMDS_MeshElement * elem;
427     if ( isNodes )
428       elem = aMesh->FindNode( *it );
429     else
430       elem = aMesh->FindElement( *it );
431     if ( !elem )
432       continue;
433
434     // Notify VERTEX sub-meshes about modification
435     if ( isNodes ) {
436       const SMDS_MeshNode* node = cast2Node( elem );
437       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438         if ( int aShapeID = node->getshapeId() )
439           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440             smmap.insert( sm );
441     }
442     // Find sub-meshes to notify about modification
443     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444     //     while ( nodeIt->more() ) {
445     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
447     //       if ( aPosition.get() ) {
448     //         if ( int aShapeID = aPosition->GetShapeId() ) {
449     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450     //             smmap.insert( sm );
451     //         }
452     //       }
453     //     }
454
455     // Do remove
456     if ( isNodes )
457       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
458     else
459       aMesh->RemoveElement( elem );
460     removed++;
461   }
462
463   // Notify sub-meshes about modification
464   if ( !smmap.empty() ) {
465     set< SMESH_subMesh *>::iterator smIt;
466     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468   }
469
470   //   // Check if the whole mesh becomes empty
471   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
473
474   return removed;
475 }
476
477 //================================================================================
478 /*!
479  * \brief Create 0D elements on all nodes of the given object.
480  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
481  *                    the all mesh is treated
482  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483  *  \param duplicateElements - to add one more 0D element to a node or not
484  */
485 //================================================================================
486
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488                                                    TIDSortedElemSet&       all0DElems,
489                                                    const bool              duplicateElements )
490 {
491   SMDS_ElemIteratorPtr elemIt;
492   if ( elements.empty() )
493   {
494     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
495   }
496   else
497   {
498     elemIt = SMESHUtils::elemSetIterator( elements );
499   }
500
501   while ( elemIt->more() )
502   {
503     const SMDS_MeshElement* e = elemIt->next();
504     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505     while ( nodeIt->more() )
506     {
507       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509       if ( duplicateElements || !it0D->more() )
510       {
511         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
512         all0DElems.insert( myLastCreatedElems.back() );
513       }
514       while ( it0D->more() )
515         all0DElems.insert( it0D->next() );
516     }
517   }
518 }
519
520 //=======================================================================
521 //function : FindShape
522 //purpose  : Return an index of the shape theElem is on
523 //           or zero if a shape not found
524 //=======================================================================
525
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
527 {
528   ClearLastCreated();
529
530   SMESHDS_Mesh * aMesh = GetMeshDS();
531   if ( aMesh->ShapeToMesh().IsNull() )
532     return 0;
533
534   int aShapeID = theElem->getshapeId();
535   if ( aShapeID < 1 )
536     return 0;
537
538   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539     if ( sm->Contains( theElem ))
540       return aShapeID;
541
542   if ( theElem->GetType() == SMDSAbs_Node ) {
543     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544   }
545   else {
546     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547   }
548
549   TopoDS_Shape aShape; // the shape a node of theElem is on
550   if ( theElem->GetType() != SMDSAbs_Node )
551   {
552     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553     while ( nodeIt->more() ) {
554       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555       if ((aShapeID = node->getshapeId()) > 0) {
556         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557           if ( sm->Contains( theElem ))
558             return aShapeID;
559           if ( aShape.IsNull() )
560             aShape = aMesh->IndexToShape( aShapeID );
561         }
562       }
563     }
564   }
565
566   // None of nodes is on a proper shape,
567   // find the shape among ancestors of aShape on which a node is
568   if ( !aShape.IsNull() ) {
569     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570     for ( ; ancIt.More(); ancIt.Next() ) {
571       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572       if ( sm && sm->Contains( theElem ))
573         return aMesh->ShapeToIndex( ancIt.Value() );
574     }
575   }
576   else
577   {
578     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579     while ( const SMESHDS_SubMesh* sm = smIt->next() )
580       if ( sm->Contains( theElem ))
581         return sm->GetID();
582   }
583
584   return 0;
585 }
586
587 //=======================================================================
588 //function : IsMedium
589 //purpose  :
590 //=======================================================================
591
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
593                                 const SMDSAbs_ElementType typeToCheck)
594 {
595   bool isMedium = false;
596   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597   while (it->more() && !isMedium ) {
598     const SMDS_MeshElement* elem = it->next();
599     isMedium = elem->IsMediumNode(node);
600   }
601   return isMedium;
602 }
603
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose  : Shift nodes in the array corresponded to quadratic triangle
607 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
609
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
611 {
612   const SMDS_MeshNode* nd1 = aNodes[0];
613   aNodes[0] = aNodes[1];
614   aNodes[1] = aNodes[2];
615   aNodes[2] = nd1;
616   const SMDS_MeshNode* nd2 = aNodes[3];
617   aNodes[3] = aNodes[4];
618   aNodes[4] = aNodes[5];
619   aNodes[5] = nd2;
620 }
621
622 //=======================================================================
623 //function : getNodesFromTwoTria
624 //purpose  : 
625 //=======================================================================
626
627 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
628                                 const SMDS_MeshElement * theTria2,
629                                 vector< const SMDS_MeshNode*>& N1,
630                                 vector< const SMDS_MeshNode*>& N2)
631 {
632   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
633   if ( N1.size() < 6 ) return false;
634   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
635   if ( N2.size() < 6 ) return false;
636
637   int sames[3] = {-1,-1,-1};
638   int nbsames = 0;
639   int i, j;
640   for(i=0; i<3; i++) {
641     for(j=0; j<3; j++) {
642       if(N1[i]==N2[j]) {
643         sames[i] = j;
644         nbsames++;
645         break;
646       }
647     }
648   }
649   if(nbsames!=2) return false;
650   if(sames[0]>-1) {
651     shiftNodesQuadTria(N1);
652     if(sames[1]>-1) {
653       shiftNodesQuadTria(N1);
654     }
655   }
656   i = sames[0] + sames[1] + sames[2];
657   for(; i<2; i++) {
658     shiftNodesQuadTria(N2);
659   }
660   // now we receive following N1 and N2 (using numeration as in the image below)
661   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
662   // i.e. first nodes from both arrays form a new diagonal
663   return true;
664 }
665
666 //=======================================================================
667 //function : InverseDiag
668 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
669 //           but having other common link.
670 //           Return False if args are improper
671 //=======================================================================
672
673 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
674                                     const SMDS_MeshElement * theTria2 )
675 {
676   ClearLastCreated();
677
678   if ( !theTria1 || !theTria2 ||
679        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
680        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
681        theTria1->GetType() != SMDSAbs_Face ||
682        theTria2->GetType() != SMDSAbs_Face )
683     return false;
684
685   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
686       (theTria2->GetEntityType() == SMDSEntity_Triangle))
687   {
688     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
689     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
690     //    |/ |                                         | \|
691     //  B +--+ 2                                     B +--+ 2
692
693     // put nodes in array and find out indices of the same ones
694     const SMDS_MeshNode* aNodes [6];
695     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
696     int i = 0;
697     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
698     while ( it->more() ) {
699       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
700
701       if ( i > 2 ) // theTria2
702         // find same node of theTria1
703         for ( int j = 0; j < 3; j++ )
704           if ( aNodes[ i ] == aNodes[ j ]) {
705             sameInd[ j ] = i;
706             sameInd[ i ] = j;
707             break;
708           }
709       // next
710       i++;
711       if ( i == 3 ) {
712         if ( it->more() )
713           return false; // theTria1 is not a triangle
714         it = theTria2->nodesIterator();
715       }
716       if ( i == 6 && it->more() )
717         return false; // theTria2 is not a triangle
718     }
719
720     // find indices of 1,2 and of A,B in theTria1
721     int iA = -1, iB = 0, i1 = 0, i2 = 0;
722     for ( i = 0; i < 6; i++ ) {
723       if ( sameInd [ i ] == -1 ) {
724         if ( i < 3 ) i1 = i;
725         else         i2 = i;
726       }
727       else if (i < 3) {
728         if ( iA >= 0) iB = i;
729         else          iA = i;
730       }
731     }
732     // nodes 1 and 2 should not be the same
733     if ( aNodes[ i1 ] == aNodes[ i2 ] )
734       return false;
735
736     // theTria1: A->2
737     aNodes[ iA ] = aNodes[ i2 ];
738     // theTria2: B->1
739     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
740
741     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
742     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
743
744     return true;
745
746   } // end if(F1 && F2)
747
748   // check case of quadratic faces
749   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
750       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
751     return false;
752   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
753       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
754     return false;
755
756   //       5
757   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
758   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
759   //    |   / |
760   //  7 +  +  + 6
761   //    | /9  |
762   //    |/    |
763   //  4 +--+--+ 3
764   //       8
765
766   vector< const SMDS_MeshNode* > N1;
767   vector< const SMDS_MeshNode* > N2;
768   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
769     return false;
770   // now we receive following N1 and N2 (using numeration as above image)
771   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
772   // i.e. first nodes from both arrays determ new diagonal
773
774   vector< const SMDS_MeshNode*> N1new( N1.size() );
775   vector< const SMDS_MeshNode*> N2new( N2.size() );
776   N1new.back() = N1.back(); // central node of biquadratic
777   N2new.back() = N2.back();
778   N1new[0] = N1[0];  N2new[0] = N1[0];
779   N1new[1] = N2[0];  N2new[1] = N1[1];
780   N1new[2] = N2[1];  N2new[2] = N2[0];
781   N1new[3] = N1[4];  N2new[3] = N1[3];
782   N1new[4] = N2[3];  N2new[4] = N2[5];
783   N1new[5] = N1[5];  N2new[5] = N1[4];
784   // change nodes in faces
785   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
786   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
787
788   // move the central node of biquadratic triangle
789   SMESH_MesherHelper helper( *GetMesh() );
790   for ( int is2nd = 0; is2nd < 2; ++is2nd )
791   {
792     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
793     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
794     if ( nodes.size() < 7 )
795       continue;
796     helper.SetSubShape( tria->getshapeId() );
797     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
798     gp_Pnt xyz;
799     if ( F.IsNull() )
800     {
801       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
802               SMESH_NodeXYZ( nodes[4] ) +
803               SMESH_NodeXYZ( nodes[5] )) / 3.;
804     }
805     else
806     {
807       bool checkUV;
808       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
809                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
810                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
811       TopLoc_Location loc;
812       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
813       xyz = S->Value( uv.X(), uv.Y() );
814       xyz.Transform( loc );
815       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
816            nodes[6]->getshapeId() > 0 )
817         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
818     }
819     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
820   }
821   return true;
822 }
823
824 //=======================================================================
825 //function : findTriangles
826 //purpose  : find triangles sharing theNode1-theNode2 link
827 //=======================================================================
828
829 static bool findTriangles(const SMDS_MeshNode *    theNode1,
830                           const SMDS_MeshNode *    theNode2,
831                           const SMDS_MeshElement*& theTria1,
832                           const SMDS_MeshElement*& theTria2)
833 {
834   if ( !theNode1 || !theNode2 ) return false;
835
836   theTria1 = theTria2 = 0;
837
838   set< const SMDS_MeshElement* > emap;
839   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
840   while (it->more()) {
841     const SMDS_MeshElement* elem = it->next();
842     if ( elem->NbCornerNodes() == 3 )
843       emap.insert( elem );
844   }
845   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
846   while (it->more()) {
847     const SMDS_MeshElement* elem = it->next();
848     if ( emap.count( elem )) {
849       if ( !theTria1 )
850       {
851         theTria1 = elem;
852       }
853       else  
854       {
855         theTria2 = elem;
856         // theTria1 must be element with minimum ID
857         if ( theTria2->GetID() < theTria1->GetID() )
858           std::swap( theTria2, theTria1 );
859         return true;
860       }
861     }
862   }
863   return false;
864 }
865
866 //=======================================================================
867 //function : InverseDiag
868 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
869 //           with ones built on the same 4 nodes but having other common link.
870 //           Return false if proper faces not found
871 //=======================================================================
872
873 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
874                                     const SMDS_MeshNode * theNode2)
875 {
876   ClearLastCreated();
877
878   const SMDS_MeshElement *tr1, *tr2;
879   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
880     return false;
881
882   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
883        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
884     return false;
885
886   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
887       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
888
889     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
890     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
891     //    |/ |                                    | \|
892     //  B +--+ 2                                B +--+ 2
893
894     // put nodes in array
895     // and find indices of 1,2 and of A in tr1 and of B in tr2
896     int i, iA1 = 0, i1 = 0;
897     const SMDS_MeshNode* aNodes1 [3];
898     SMDS_ElemIteratorPtr it;
899     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
900       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
901       if ( aNodes1[ i ] == theNode1 )
902         iA1 = i; // node A in tr1
903       else if ( aNodes1[ i ] != theNode2 )
904         i1 = i;  // node 1
905     }
906     int iB2 = 0, i2 = 0;
907     const SMDS_MeshNode* aNodes2 [3];
908     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
909       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
910       if ( aNodes2[ i ] == theNode2 )
911         iB2 = i; // node B in tr2
912       else if ( aNodes2[ i ] != theNode1 )
913         i2 = i;  // node 2
914     }
915
916     // nodes 1 and 2 should not be the same
917     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
918       return false;
919
920     // tr1: A->2
921     aNodes1[ iA1 ] = aNodes2[ i2 ];
922     // tr2: B->1
923     aNodes2[ iB2 ] = aNodes1[ i1 ];
924
925     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
926     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
927
928     return true;
929   }
930
931   // check case of quadratic faces
932   return InverseDiag(tr1,tr2);
933 }
934
935 //=======================================================================
936 //function : getQuadrangleNodes
937 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
938 //           fusion of triangles tr1 and tr2 having shared link on
939 //           theNode1 and theNode2
940 //=======================================================================
941
942 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
943                         const SMDS_MeshNode *    theNode1,
944                         const SMDS_MeshNode *    theNode2,
945                         const SMDS_MeshElement * tr1,
946                         const SMDS_MeshElement * tr2 )
947 {
948   if( tr1->NbNodes() != tr2->NbNodes() )
949     return false;
950   // find the 4-th node to insert into tr1
951   const SMDS_MeshNode* n4 = 0;
952   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
953   int i=0;
954   while ( !n4 && i<3 ) {
955     const SMDS_MeshNode * n = cast2Node( it->next() );
956     i++;
957     bool isDiag = ( n == theNode1 || n == theNode2 );
958     if ( !isDiag )
959       n4 = n;
960   }
961   // Make an array of nodes to be in a quadrangle
962   int iNode = 0, iFirstDiag = -1;
963   it = tr1->nodesIterator();
964   i=0;
965   while ( i<3 ) {
966     const SMDS_MeshNode * n = cast2Node( it->next() );
967     i++;
968     bool isDiag = ( n == theNode1 || n == theNode2 );
969     if ( isDiag ) {
970       if ( iFirstDiag < 0 )
971         iFirstDiag = iNode;
972       else if ( iNode - iFirstDiag == 1 )
973         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
974     }
975     else if ( n == n4 ) {
976       return false; // tr1 and tr2 should not have all the same nodes
977     }
978     theQuadNodes[ iNode++ ] = n;
979   }
980   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
981     theQuadNodes[ iNode ] = n4;
982
983   return true;
984 }
985
986 //=======================================================================
987 //function : DeleteDiag
988 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
989 //           with a quadrangle built on the same 4 nodes.
990 //           Return false if proper faces not found
991 //=======================================================================
992
993 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
994                                    const SMDS_MeshNode * theNode2)
995 {
996   ClearLastCreated();
997
998   const SMDS_MeshElement *tr1, *tr2;
999   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1000     return false;
1001
1002   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1003        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1004     return false;
1005
1006   SMESHDS_Mesh * aMesh = GetMeshDS();
1007
1008   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1009       (tr2->GetEntityType() == SMDSEntity_Triangle))
1010   {
1011     const SMDS_MeshNode* aNodes [ 4 ];
1012     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1013       return false;
1014
1015     const SMDS_MeshElement* newElem = 0;
1016     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1017     myLastCreatedElems.push_back(newElem);
1018     AddToSameGroups( newElem, tr1, aMesh );
1019     int aShapeId = tr1->getshapeId();
1020     if ( aShapeId )
1021       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1022
1023     aMesh->RemoveElement( tr1 );
1024     aMesh->RemoveElement( tr2 );
1025
1026     return true;
1027   }
1028
1029   // check case of quadratic faces
1030   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1031     return false;
1032   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1033     return false;
1034
1035   //       5
1036   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1037   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1038   //    |   / |
1039   //  7 +  +  + 6
1040   //    | /9  |
1041   //    |/    |
1042   //  4 +--+--+ 3
1043   //       8
1044
1045   vector< const SMDS_MeshNode* > N1;
1046   vector< const SMDS_MeshNode* > N2;
1047   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1048     return false;
1049   // now we receive following N1 and N2 (using numeration as above image)
1050   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1051   // i.e. first nodes from both arrays determ new diagonal
1052
1053   const SMDS_MeshNode* aNodes[8];
1054   aNodes[0] = N1[0];
1055   aNodes[1] = N1[1];
1056   aNodes[2] = N2[0];
1057   aNodes[3] = N2[1];
1058   aNodes[4] = N1[3];
1059   aNodes[5] = N2[5];
1060   aNodes[6] = N2[3];
1061   aNodes[7] = N1[5];
1062
1063   const SMDS_MeshElement* newElem = 0;
1064   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1065                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1066   myLastCreatedElems.push_back(newElem);
1067   AddToSameGroups( newElem, tr1, aMesh );
1068   int aShapeId = tr1->getshapeId();
1069   if ( aShapeId )
1070   {
1071     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1072   }
1073   aMesh->RemoveElement( tr1 );
1074   aMesh->RemoveElement( tr2 );
1075
1076   // remove middle node (9)
1077   GetMeshDS()->RemoveNode( N1[4] );
1078
1079   return true;
1080 }
1081
1082 //=======================================================================
1083 //function : Reorient
1084 //purpose  : Reverse theElement orientation
1085 //=======================================================================
1086
1087 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1088 {
1089   ClearLastCreated();
1090
1091   if (!theElem)
1092     return false;
1093   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1094   if ( !it || !it->more() )
1095     return false;
1096
1097   const SMDSAbs_ElementType type = theElem->GetType();
1098   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1099     return false;
1100
1101   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1102   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1103   {
1104     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1105     if (!aPolyedre) {
1106       MESSAGE("Warning: bad volumic element");
1107       return false;
1108     }
1109     SMDS_VolumeTool vTool( aPolyedre );
1110     const int nbFaces = vTool.NbFaces();
1111     vector<int> quantities( nbFaces );
1112     vector<const SMDS_MeshNode *> poly_nodes;
1113
1114     // check if all facets are oriented equally
1115     bool sameOri = true;
1116     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1117     for (int iface = 0; iface < nbFaces; iface++)
1118     {
1119       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1120       if ( facetOri[ iface ] != facetOri[ 0 ])
1121         sameOri = false;
1122     }
1123
1124     // reverse faces of the polyhedron
1125     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1126     poly_nodes.reserve( vTool.NbNodes() );
1127     for ( int iface = 0; iface < nbFaces; iface++ )
1128     {
1129       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1130       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1131       bool toReverse = ( facetOri[ iface ] != neededOri );
1132
1133       quantities[ iface ] = nbFaceNodes;
1134
1135       if ( toReverse )
1136         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1137           poly_nodes.push_back( nodes[ inode ]);
1138       else
1139         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1140     }
1141     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1142   }
1143   else // other elements
1144   {
1145     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1146     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1147     if ( interlace.empty() )
1148     {
1149       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1150     }
1151     else
1152     {
1153       SMDS_MeshCell::applyInterlace( interlace, nodes );
1154     }
1155     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1156   }
1157   return false;
1158 }
1159
1160 //================================================================================
1161 /*!
1162  * \brief Reorient faces.
1163  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1164  * \param theDirection - desired direction of normal of \a theFace
1165  * \param theFace - one of \a theFaces that should be oriented according to
1166  *        \a theDirection and whose orientation defines orientation of other faces
1167  * \return number of reoriented faces.
1168  */
1169 //================================================================================
1170
1171 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1172                                   const gp_Dir&            theDirection,
1173                                   const SMDS_MeshElement * theFace)
1174 {
1175   int nbReori = 0;
1176   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1177
1178   if ( theFaces.empty() )
1179   {
1180     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1181     while ( fIt->more() )
1182       theFaces.insert( theFaces.end(), fIt->next() );
1183   }
1184
1185   // orient theFace according to theDirection
1186   gp_XYZ normal;
1187   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1188   if ( normal * theDirection.XYZ() < 0 )
1189     nbReori += Reorient( theFace );
1190
1191   // Orient other faces
1192
1193   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1194   TIDSortedElemSet avoidSet;
1195   set< SMESH_TLink > checkedLinks;
1196   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1197
1198   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1199     theFaces.erase( theFace );
1200   startFaces.insert( theFace );
1201
1202   int nodeInd1, nodeInd2;
1203   const SMDS_MeshElement*           otherFace;
1204   vector< const SMDS_MeshElement* > facesNearLink;
1205   vector< std::pair< int, int > >   nodeIndsOfFace;
1206
1207   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1208   while ( !startFaces.empty() )
1209   {
1210     startFace = startFaces.begin();
1211     theFace = *startFace;
1212     startFaces.erase( startFace );
1213     if ( !visitedFaces.insert( theFace ).second )
1214       continue;
1215
1216     avoidSet.clear();
1217     avoidSet.insert(theFace);
1218
1219     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1220
1221     const int nbNodes = theFace->NbCornerNodes();
1222     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1223     {
1224       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1225       linkIt_isNew = checkedLinks.insert( link );
1226       if ( !linkIt_isNew.second )
1227       {
1228         // link has already been checked and won't be encountered more
1229         // if the group (theFaces) is manifold
1230         //checkedLinks.erase( linkIt_isNew.first );
1231       }
1232       else
1233       {
1234         facesNearLink.clear();
1235         nodeIndsOfFace.clear();
1236         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1237                                                              theFaces, avoidSet,
1238                                                              &nodeInd1, &nodeInd2 )))
1239           if ( otherFace != theFace)
1240           {
1241             facesNearLink.push_back( otherFace );
1242             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1243             avoidSet.insert( otherFace );
1244           }
1245         if ( facesNearLink.size() > 1 )
1246         {
1247           // NON-MANIFOLD mesh shell !
1248           // select a face most co-directed with theFace,
1249           // other faces won't be visited this time
1250           gp_XYZ NF, NOF;
1251           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1252           double proj, maxProj = -1;
1253           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1254             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1255             if (( proj = Abs( NF * NOF )) > maxProj ) {
1256               maxProj = proj;
1257               otherFace = facesNearLink[i];
1258               nodeInd1  = nodeIndsOfFace[i].first;
1259               nodeInd2  = nodeIndsOfFace[i].second;
1260             }
1261           }
1262           // not to visit rejected faces
1263           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1264             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1265               visitedFaces.insert( facesNearLink[i] );
1266         }
1267         else if ( facesNearLink.size() == 1 )
1268         {
1269           otherFace = facesNearLink[0];
1270           nodeInd1  = nodeIndsOfFace.back().first;
1271           nodeInd2  = nodeIndsOfFace.back().second;
1272         }
1273         if ( otherFace && otherFace != theFace)
1274         {
1275           // link must be reverse in otherFace if orientation to otherFace
1276           // is same as that of theFace
1277           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1278           {
1279             nbReori += Reorient( otherFace );
1280           }
1281           startFaces.insert( otherFace );
1282         }
1283       }
1284       std::swap( link.first, link.second ); // reverse the link
1285     }
1286   }
1287   return nbReori;
1288 }
1289
1290 //================================================================================
1291 /*!
1292  * \brief Reorient faces basing on orientation of adjacent volumes.
1293  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1294  * \param theVolumes - reference volumes.
1295  * \param theOutsideNormal - to orient faces to have their normal
1296  *        pointing either \a outside or \a inside the adjacent volumes.
1297  * \return number of reoriented faces.
1298  */
1299 //================================================================================
1300
1301 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1302                                       TIDSortedElemSet & theVolumes,
1303                                       const bool         theOutsideNormal)
1304 {
1305   int nbReori = 0;
1306
1307   SMDS_ElemIteratorPtr faceIt;
1308   if ( theFaces.empty() )
1309     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1310   else
1311     faceIt = SMESHUtils::elemSetIterator( theFaces );
1312
1313   vector< const SMDS_MeshNode* > faceNodes;
1314   TIDSortedElemSet checkedVolumes;
1315   set< const SMDS_MeshNode* > faceNodesSet;
1316   SMDS_VolumeTool volumeTool;
1317
1318   while ( faceIt->more() ) // loop on given faces
1319   {
1320     const SMDS_MeshElement* face = faceIt->next();
1321     if ( face->GetType() != SMDSAbs_Face )
1322       continue;
1323
1324     const size_t nbCornersNodes = face->NbCornerNodes();
1325     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1326
1327     checkedVolumes.clear();
1328     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1329     while ( vIt->more() )
1330     {
1331       const SMDS_MeshElement* volume = vIt->next();
1332
1333       if ( !checkedVolumes.insert( volume ).second )
1334         continue;
1335       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336         continue;
1337
1338       // is volume adjacent?
1339       bool allNodesCommon = true;
1340       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1341         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1342       if ( !allNodesCommon )
1343         continue;
1344
1345       // get nodes of a corresponding volume facet
1346       faceNodesSet.clear();
1347       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1348       volumeTool.Set( volume );
1349       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1350       if ( facetID < 0 ) continue;
1351       volumeTool.SetExternalNormal();
1352       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1353
1354       // compare order of faceNodes and facetNodes
1355       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1356       int iNN[2];
1357       for ( int i = 0; i < 2; ++i )
1358       {
1359         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1360         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1361           if ( faceNodes[ iN ] == n )
1362           {
1363             iNN[ i ] = iN;
1364             break;
1365           }
1366       }
1367       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1368       if ( isOutside != theOutsideNormal )
1369         nbReori += Reorient( face );
1370     }
1371   }  // loop on given faces
1372
1373   return nbReori;
1374 }
1375
1376 //=======================================================================
1377 //function : getBadRate
1378 //purpose  :
1379 //=======================================================================
1380
1381 static double getBadRate (const SMDS_MeshElement*               theElem,
1382                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1383 {
1384   SMESH::Controls::TSequenceOfXYZ P;
1385   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1386     return 1e100;
1387   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1388   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 }
1390
1391 //=======================================================================
1392 //function : QuadToTri
1393 //purpose  : Cut quadrangles into triangles.
1394 //           theCrit is used to select a diagonal to cut
1395 //=======================================================================
1396
1397 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1398                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1399 {
1400   ClearLastCreated();
1401
1402   if ( !theCrit.get() )
1403     return false;
1404
1405   SMESHDS_Mesh *       aMesh = GetMeshDS();
1406   Handle(Geom_Surface) surface;
1407   SMESH_MesherHelper   helper( *GetMesh() );
1408
1409   myLastCreatedElems.reserve( theElems.size() * 2 );
1410
1411   TIDSortedElemSet::iterator itElem;
1412   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1413   {
1414     const SMDS_MeshElement* elem = *itElem;
1415     if ( !elem || elem->GetType() != SMDSAbs_Face )
1416       continue;
1417     if ( elem->NbCornerNodes() != 4 )
1418       continue;
1419
1420     // retrieve element nodes
1421     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1422
1423     // compare two sets of possible triangles
1424     double aBadRate1, aBadRate2; // to what extent a set is bad
1425     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1426     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1427     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1428
1429     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1430     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1431     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1432
1433     const int aShapeId = FindShape( elem );
1434     const SMDS_MeshElement* newElem1 = 0;
1435     const SMDS_MeshElement* newElem2 = 0;
1436
1437     if ( !elem->IsQuadratic() ) // split linear quadrangle
1438     {
1439       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1440       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1441       if ( aBadRate1 <= aBadRate2 ) {
1442         // tr1 + tr2 is better
1443         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1444         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445       }
1446       else {
1447         // tr3 + tr4 is better
1448         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1449         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450       }
1451     }
1452     else // split quadratic quadrangle
1453     {
1454       helper.SetIsQuadratic( true );
1455       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1456
1457       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1458       if ( aNodes.size() == 9 )
1459       {
1460         helper.SetIsBiQuadratic( true );
1461         if ( aBadRate1 <= aBadRate2 )
1462           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1463         else
1464           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1465       }
1466       // create a new element
1467       if ( aBadRate1 <= aBadRate2 ) {
1468         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1469         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470       }
1471       else {
1472         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1473         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1474       }
1475     } // quadratic case
1476
1477     // care of a new element
1478
1479     myLastCreatedElems.push_back(newElem1);
1480     myLastCreatedElems.push_back(newElem2);
1481     AddToSameGroups( newElem1, elem, aMesh );
1482     AddToSameGroups( newElem2, elem, aMesh );
1483
1484     // put a new triangle on the same shape
1485     if ( aShapeId )
1486       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1487     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1488
1489     aMesh->RemoveElement( elem );
1490   }
1491   return true;
1492 }
1493
1494 //=======================================================================
1495 /*!
1496  * \brief Split each of given quadrangles into 4 triangles.
1497  * \param theElems - The faces to be split. If empty all faces are split.
1498  */
1499 //=======================================================================
1500
1501 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1502 {
1503   ClearLastCreated();
1504   myLastCreatedElems.reserve( theElems.size() * 4 );
1505
1506   SMESH_MesherHelper helper( *GetMesh() );
1507   helper.SetElementsOnShape( true );
1508
1509   // get standalone groups of faces
1510   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1511   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1512     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1513       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1514         allFaceGroups.push_back( & group->SMDSGroup() );
1515
1516   bool   checkUV;
1517   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1518   gp_XYZ xyz[9];
1519   vector< const SMDS_MeshNode* > nodes;
1520   SMESHDS_SubMesh*               subMeshDS = 0;
1521   TopoDS_Face                    F;
1522   Handle(Geom_Surface)           surface;
1523   TopLoc_Location                loc;
1524
1525   SMDS_ElemIteratorPtr faceIt;
1526   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1527   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1528
1529   while ( faceIt->more() )
1530   {
1531     const SMDS_MeshElement* quad = faceIt->next();
1532     if ( !quad || quad->NbCornerNodes() != 4 )
1533       continue;
1534
1535     // get a surface the quad is on
1536
1537     if ( quad->getshapeId() < 1 )
1538     {
1539       F.Nullify();
1540       helper.SetSubShape( 0 );
1541       subMeshDS = 0;
1542     }
1543     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1544     {
1545       helper.SetSubShape( quad->getshapeId() );
1546       if ( !helper.GetSubShape().IsNull() &&
1547            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1548       {
1549         F = TopoDS::Face( helper.GetSubShape() );
1550         surface = BRep_Tool::Surface( F, loc );
1551         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552       }
1553       else
1554       {
1555         helper.SetSubShape( 0 );
1556         subMeshDS = 0;
1557       }
1558     }
1559
1560     // create a central node
1561
1562     const SMDS_MeshNode* nCentral;
1563     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1564
1565     if ( nodes.size() == 9 )
1566     {
1567       nCentral = nodes.back();
1568     }
1569     else
1570     {
1571       size_t iN = 0;
1572       if ( F.IsNull() )
1573       {
1574         for ( ; iN < nodes.size(); ++iN )
1575           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1576
1577         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1579
1580         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581                                    xyz[0], xyz[1], xyz[2], xyz[3],
1582                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1583       }
1584       else
1585       {
1586         for ( ; iN < nodes.size(); ++iN )
1587           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1588
1589         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1591
1592         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593                                   uv[0], uv[1], uv[2], uv[3],
1594                                   uv[4], uv[5], uv[6], uv[7] );
1595
1596         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597         xyz[ 8 ] = p.XYZ();
1598       }
1599
1600       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601                                  uv[8].X(), uv[8].Y() );
1602       myLastCreatedNodes.push_back( nCentral );
1603     }
1604
1605     helper.SetIsQuadratic  ( nodes.size() > 4 );
1606     helper.SetIsBiQuadratic( nodes.size() == 9 );
1607     if ( helper.GetIsQuadratic() )
1608       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609
1610     // select groups to update
1611     faceGroups.clear();
1612     for ( SMDS_MeshGroup* group : allFaceGroups )
1613       if ( group->Remove( quad ))
1614         faceGroups.push_back( group );
1615
1616     // create 4 triangles
1617
1618     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1619
1620     for ( int i = 0; i < 4; ++i )
1621     {
1622       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1623                                                nodes[(i+1)%4],
1624                                                nCentral );
1625       myLastCreatedElems.push_back( tria );
1626       for ( SMDS_MeshGroup* group : faceGroups )
1627         group->Add( tria );
1628     }
1629   }
1630 }
1631
1632 //=======================================================================
1633 //function : BestSplit
1634 //purpose  : Find better diagonal for cutting.
1635 //=======================================================================
1636
1637 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1638                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1639 {
1640   ClearLastCreated();
1641
1642   if (!theCrit.get())
1643     return -1;
1644
1645   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1646     return -1;
1647
1648   if( theQuad->NbNodes()==4 ||
1649       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1650
1651     // retrieve element nodes
1652     const SMDS_MeshNode* aNodes [4];
1653     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1654     int i = 0;
1655     //while (itN->more())
1656     while (i<4) {
1657       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1658     }
1659     // compare two sets of possible triangles
1660     double aBadRate1, aBadRate2; // to what extent a set is bad
1661     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1662     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1663     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1664
1665     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1666     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1667     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1668     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1669     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1670     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1671       return 1; // diagonal 1-3
1672
1673     return 2; // diagonal 2-4
1674   }
1675   return -1;
1676 }
1677
1678 namespace
1679 {
1680   // Methods of splitting volumes into tetra
1681
1682   const int theHexTo5_1[5*4+1] =
1683     {
1684       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1685     };
1686   const int theHexTo5_2[5*4+1] =
1687     {
1688       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1689     };
1690   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1691
1692   const int theHexTo6_1[6*4+1] =
1693     {
1694       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
1695     };
1696   const int theHexTo6_2[6*4+1] =
1697     {
1698       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
1699     };
1700   const int theHexTo6_3[6*4+1] =
1701     {
1702       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
1703     };
1704   const int theHexTo6_4[6*4+1] =
1705     {
1706       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
1707     };
1708   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1709
1710   const int thePyraTo2_1[2*4+1] =
1711     {
1712       0, 1, 2, 4,    0, 2, 3, 4,   -1
1713     };
1714   const int thePyraTo2_2[2*4+1] =
1715     {
1716       1, 2, 3, 4,    1, 3, 0, 4,   -1
1717     };
1718   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1719
1720   const int thePentaTo3_1[3*4+1] =
1721     {
1722       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1723     };
1724   const int thePentaTo3_2[3*4+1] =
1725     {
1726       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1727     };
1728   const int thePentaTo3_3[3*4+1] =
1729     {
1730       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1731     };
1732   const int thePentaTo3_4[3*4+1] =
1733     {
1734       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1735     };
1736   const int thePentaTo3_5[3*4+1] =
1737     {
1738       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1739     };
1740   const int thePentaTo3_6[3*4+1] =
1741     {
1742       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1743     };
1744   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1745                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1746
1747   // Methods of splitting hexahedron into prisms
1748
1749   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1750     {
1751       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
1752     };
1753   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1754     {
1755       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
1756     };
1757   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1758     {
1759       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
1760     };
1761
1762   const int theHexTo2Prisms_BT_1[6*2+1] =
1763     {
1764       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1765     };
1766   const int theHexTo2Prisms_BT_2[6*2+1] =
1767     {
1768       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1769     };
1770   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1771
1772   const int theHexTo2Prisms_LR_1[6*2+1] =
1773     {
1774       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1775     };
1776   const int theHexTo2Prisms_LR_2[6*2+1] =
1777     {
1778       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1779     };
1780   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1781
1782   const int theHexTo2Prisms_FB_1[6*2+1] =
1783     {
1784       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1785     };
1786   const int theHexTo2Prisms_FB_2[6*2+1] =
1787     {
1788       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1789     };
1790   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1791
1792
1793   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1794   {
1795     int _n1, _n2, _n3;
1796     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1797     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1798     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1799                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1800   };
1801   struct TSplitMethod
1802   {
1803     int        _nbSplits;
1804     int        _nbCorners;
1805     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1806     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1807     bool       _ownConn;      //!< to delete _connectivity in destructor
1808     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1809
1810     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1811       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1812     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1813     TSplitMethod(const TSplitMethod &splitMethod)
1814       : _nbSplits(splitMethod._nbSplits),
1815         _nbCorners(splitMethod._nbCorners),
1816         _baryNode(splitMethod._baryNode),
1817         _ownConn(splitMethod._ownConn),
1818         _faceBaryNode(splitMethod._faceBaryNode)
1819     {
1820       _connectivity = splitMethod._connectivity;
1821       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1822       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1823     }
1824     bool hasFacet( const TTriangleFacet& facet ) const
1825     {
1826       if ( _nbCorners == 4 )
1827       {
1828         const int* tetConn = _connectivity;
1829         for ( ; tetConn[0] >= 0; tetConn += 4 )
1830           if (( facet.contains( tetConn[0] ) +
1831                 facet.contains( tetConn[1] ) +
1832                 facet.contains( tetConn[2] ) +
1833                 facet.contains( tetConn[3] )) == 3 )
1834             return true;
1835       }
1836       else // prism, _nbCorners == 6
1837       {
1838         const int* prismConn = _connectivity;
1839         for ( ; prismConn[0] >= 0; prismConn += 6 )
1840         {
1841           if (( facet.contains( prismConn[0] ) &&
1842                 facet.contains( prismConn[1] ) &&
1843                 facet.contains( prismConn[2] ))
1844               ||
1845               ( facet.contains( prismConn[3] ) &&
1846                 facet.contains( prismConn[4] ) &&
1847                 facet.contains( prismConn[5] )))
1848             return true;
1849         }
1850       }
1851       return false;
1852     }
1853   };
1854
1855   //=======================================================================
1856   /*!
1857    * \brief return TSplitMethod for the given element to split into tetrahedra
1858    */
1859   //=======================================================================
1860
1861   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1862   {
1863     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1864
1865     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1866     // an edge and a face barycenter; tertaherdons are based on triangles and
1867     // a volume barycenter
1868     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1869
1870     // Find out how adjacent volumes are split
1871
1872     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1873     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1874     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1875     {
1876       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1877       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1878       if ( nbNodes < 4 ) continue;
1879
1880       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1881       const int* nInd = vol.GetFaceNodesIndices( iF );
1882       if ( nbNodes == 4 )
1883       {
1884         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1885         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1886         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1887         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1888       }
1889       else
1890       {
1891         int iCom = 0; // common node of triangle faces to split into
1892         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1893         {
1894           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1895                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1896                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1897           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1898                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1899                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1900           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1901           {
1902             triaSplits.push_back( t012 );
1903             triaSplits.push_back( t023 );
1904             break;
1905           }
1906         }
1907       }
1908       if ( !triaSplits.empty() )
1909         hasAdjacentSplits = true;
1910     }
1911
1912     // Among variants of split method select one compliant with adjacent volumes
1913
1914     TSplitMethod method;
1915     if ( !vol.Element()->IsPoly() && !is24TetMode )
1916     {
1917       int nbVariants = 2, nbTet = 0;
1918       const int** connVariants = 0;
1919       switch ( vol.Element()->GetEntityType() )
1920       {
1921       case SMDSEntity_Hexa:
1922       case SMDSEntity_Quad_Hexa:
1923       case SMDSEntity_TriQuad_Hexa:
1924         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1925           connVariants = theHexTo5, nbTet = 5;
1926         else
1927           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1928         break;
1929       case SMDSEntity_Pyramid:
1930       case SMDSEntity_Quad_Pyramid:
1931         connVariants = thePyraTo2;  nbTet = 2;
1932         break;
1933       case SMDSEntity_Penta:
1934       case SMDSEntity_Quad_Penta:
1935       case SMDSEntity_BiQuad_Penta:
1936         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1937         break;
1938       default:
1939         nbVariants = 0;
1940       }
1941       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1942       {
1943         // check method compliance with adjacent tetras,
1944         // all found splits must be among facets of tetras described by this method
1945         method = TSplitMethod( nbTet, connVariants[variant] );
1946         if ( hasAdjacentSplits && method._nbSplits > 0 )
1947         {
1948           bool facetCreated = true;
1949           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1950           {
1951             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1952             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1953               facetCreated = method.hasFacet( *facet );
1954           }
1955           if ( !facetCreated )
1956             method = TSplitMethod(0); // incompatible method
1957         }
1958       }
1959     }
1960     if ( method._nbSplits < 1 )
1961     {
1962       // No standard method is applicable, use a generic solution:
1963       // each facet of a volume is split into triangles and
1964       // each of triangles and a volume barycenter form a tetrahedron.
1965
1966       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1967
1968       int* connectivity = new int[ maxTetConnSize + 1 ];
1969       method._connectivity = connectivity;
1970       method._ownConn = true;
1971       method._baryNode = !isHex27; // to create central node or not
1972
1973       int connSize = 0;
1974       int baryCenInd = vol.NbNodes() - int( isHex27 );
1975       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1976       {
1977         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1978         const int*   nInd = vol.GetFaceNodesIndices( iF );
1979         // find common node of triangle facets of tetra to create
1980         int iCommon = 0; // index in linear numeration
1981         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1982         if ( !triaSplits.empty() )
1983         {
1984           // by found facets
1985           const TTriangleFacet* facet = &triaSplits.front();
1986           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1987             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1988                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1989               break;
1990         }
1991         else if ( nbNodes > 3 && !is24TetMode )
1992         {
1993           // find the best method of splitting into triangles by aspect ratio
1994           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1995           map< double, int > badness2iCommon;
1996           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1997           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1998           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1999           {
2000             double badness = 0;
2001             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2002             {
2003               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2004                                       nodes[ iQ*((iLast-1)%nbNodes)],
2005                                       nodes[ iQ*((iLast  )%nbNodes)]);
2006               badness += getBadRate( &tria, aspectRatio );
2007             }
2008             badness2iCommon.insert( make_pair( badness, iCommon ));
2009           }
2010           // use iCommon with lowest badness
2011           iCommon = badness2iCommon.begin()->second;
2012         }
2013         if ( iCommon >= nbNodes )
2014           iCommon = 0; // something wrong
2015
2016         // fill connectivity of tetrahedra based on a current face
2017         int nbTet = nbNodes - 2;
2018         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2019         {
2020           int faceBaryCenInd;
2021           if ( isHex27 )
2022           {
2023             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2024             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2025           }
2026           else
2027           {
2028             method._faceBaryNode[ iF ] = 0;
2029             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2030           }
2031           nbTet = nbNodes;
2032           for ( int i = 0; i < nbTet; ++i )
2033           {
2034             int i1 = i, i2 = (i+1) % nbNodes;
2035             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2036             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038             connectivity[ connSize++ ] = faceBaryCenInd;
2039             connectivity[ connSize++ ] = baryCenInd;
2040           }
2041         }
2042         else
2043         {
2044           for ( int i = 0; i < nbTet; ++i )
2045           {
2046             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2047             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2048             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2049             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2050             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2051             connectivity[ connSize++ ] = baryCenInd;
2052           }
2053         }
2054         method._nbSplits += nbTet;
2055
2056       } // loop on volume faces
2057
2058       connectivity[ connSize++ ] = -1;
2059
2060     } // end of generic solution
2061
2062     return method;
2063   }
2064   //=======================================================================
2065   /*!
2066    * \brief return TSplitMethod to split haxhedron into prisms
2067    */
2068   //=======================================================================
2069
2070   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2071                                     const int        methodFlags,
2072                                     const int        facetToSplit)
2073   {
2074     TSplitMethod method;
2075
2076     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2077     // B, T, L, B, R, F
2078     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2079
2080     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2081     {
2082       static TSplitMethod to4methods[4]; // order BT, LR, FB
2083       if ( to4methods[iF]._nbSplits == 0 )
2084       {
2085         switch ( iF ) {
2086         case 0:
2087           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2088           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2089           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2090           break;
2091         case 1:
2092           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2093           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2094           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2095           break;
2096         case 2:
2097           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2098           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2099           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2100           break;
2101         default: return to4methods[3];
2102         }
2103         to4methods[iF]._nbSplits  = 4;
2104         to4methods[iF]._nbCorners = 6;
2105       }
2106       method = to4methods[iF];
2107       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2108       return method;
2109     }
2110     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2111
2112     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2113
2114     const int nbVariants = 2, nbSplits = 2;
2115     const int** connVariants = 0;
2116     switch ( iF ) {
2117     case 0: connVariants = theHexTo2Prisms_BT; break;
2118     case 1: connVariants = theHexTo2Prisms_LR; break;
2119     case 2: connVariants = theHexTo2Prisms_FB; break;
2120     default: return method;
2121     }
2122
2123     // look for prisms adjacent via facetToSplit and an opposite one
2124     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2125     {
2126       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2128       if ( nbNodes != 4 ) return method;
2129
2130       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2131       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2132       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2133       TTriangleFacet* t;
2134       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2135         t = &t012;
2136       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2137         t = &t123;
2138       else
2139         continue;
2140
2141       // there are adjacent prism
2142       for ( int variant = 0; variant < nbVariants; ++variant )
2143       {
2144         // check method compliance with adjacent prisms,
2145         // the found prism facets must be among facets of prisms described by current method
2146         method._nbSplits     = nbSplits;
2147         method._nbCorners    = 6;
2148         method._connectivity = connVariants[ variant ];
2149         if ( method.hasFacet( *t ))
2150           return method;
2151       }
2152     }
2153
2154     // No adjacent prisms. Select a variant with a best aspect ratio.
2155
2156     double badness[2] = { 0., 0. };
2157     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2158     const SMDS_MeshNode** nodes = vol.GetNodes();
2159     for ( int variant = 0; variant < nbVariants; ++variant )
2160       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2161       {
2162         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2163         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2164
2165         method._connectivity = connVariants[ variant ];
2166         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2167         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2168         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2169
2170         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2171                                 nodes[ t->_n2 ],
2172                                 nodes[ t->_n3 ] );
2173         badness[ variant ] += getBadRate( &tria, aspectRatio );
2174       }
2175     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2176
2177     method._nbSplits     = nbSplits;
2178     method._nbCorners    = 6;
2179     method._connectivity = connVariants[ iBetter ];
2180
2181     return method;
2182   }
2183
2184   //================================================================================
2185   /*!
2186    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2187    */
2188   //================================================================================
2189
2190   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2191                                        const SMDSAbs_GeometryType geom ) const
2192   {
2193     // find the tetrahedron including the three nodes of facet
2194     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2195     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2196     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2197     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2198     while ( volIt1->more() )
2199     {
2200       const SMDS_MeshElement* v = volIt1->next();
2201       if ( v->GetGeomType() != geom )
2202         continue;
2203       const int lastCornerInd = v->NbCornerNodes() - 1;
2204       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2205         continue; // medium node not allowed
2206       const int ind2 = v->GetNodeIndex( n2 );
2207       if ( ind2 < 0 || lastCornerInd < ind2 )
2208         continue;
2209       const int ind3 = v->GetNodeIndex( n3 );
2210       if ( ind3 < 0 || lastCornerInd < ind3 )
2211         continue;
2212       return true;
2213     }
2214     return false;
2215   }
2216
2217   //=======================================================================
2218   /*!
2219    * \brief A key of a face of volume
2220    */
2221   //=======================================================================
2222
2223   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2224   {
2225     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2226     {
2227       TIDSortedNodeSet sortedNodes;
2228       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2229       int nbNodes = vol.NbFaceNodes( iF );
2230       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2231       for ( int i = 0; i < nbNodes; i += iQ )
2232         sortedNodes.insert( fNodes[i] );
2233       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2234       first.first   = (*(n++))->GetID();
2235       first.second  = (*(n++))->GetID();
2236       second.first  = (*(n++))->GetID();
2237       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2238     }
2239   };
2240 } // namespace
2241
2242 //=======================================================================
2243 //function : SplitVolumes
2244 //purpose  : Split volume elements into tetrahedra or prisms.
2245 //           If facet ID < 0, element is split into tetrahedra,
2246 //           else a hexahedron is split into prisms so that the given facet is
2247 //           split into triangles
2248 //=======================================================================
2249
2250 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2251                                      const int            theMethodFlags)
2252 {
2253   SMDS_VolumeTool    volTool;
2254   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2255   fHelper.ToFixNodeParameters( true );
2256
2257   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2258   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2259
2260   SMESH_SequenceOfElemPtr newNodes, newElems;
2261
2262   // map face of volume to it's baricenrtic node
2263   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2264   double bc[3];
2265   vector<const SMDS_MeshElement* > splitVols;
2266
2267   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2268   for ( ; elem2facet != theElems.end(); ++elem2facet )
2269   {
2270     const SMDS_MeshElement* elem = elem2facet->first;
2271     const int       facetToSplit = elem2facet->second;
2272     if ( elem->GetType() != SMDSAbs_Volume )
2273       continue;
2274     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2275     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2276       continue;
2277
2278     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2279
2280     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2281                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2282                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2283     if ( splitMethod._nbSplits < 1 ) continue;
2284
2285     // find submesh to add new tetras to
2286     if ( !subMesh || !subMesh->Contains( elem ))
2287     {
2288       int shapeID = FindShape( elem );
2289       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2290       subMesh = GetMeshDS()->MeshElements( shapeID );
2291     }
2292     int iQ;
2293     if ( elem->IsQuadratic() )
2294     {
2295       iQ = 2;
2296       // add quadratic links to the helper
2297       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2298       {
2299         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2300         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2301         for ( int iN = 0; iN < nbN; iN += iQ )
2302           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2303       }
2304       helper.SetIsQuadratic( true );
2305     }
2306     else
2307     {
2308       iQ = 1;
2309       helper.SetIsQuadratic( false );
2310     }
2311     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2312                                         volTool.GetNodes() + elem->NbNodes() );
2313     helper.SetElementsOnShape( true );
2314     if ( splitMethod._baryNode )
2315     {
2316       // make a node at barycenter
2317       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2318       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2319       nodes.push_back( gcNode );
2320       newNodes.push_back( gcNode );
2321     }
2322     if ( !splitMethod._faceBaryNode.empty() )
2323     {
2324       // make or find baricentric nodes of faces
2325       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2326       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2327       {
2328         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2329           volFace2BaryNode.insert
2330           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2331         if ( !f_n->second )
2332         {
2333           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2334           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2335         }
2336         nodes.push_back( iF_n->second = f_n->second );
2337       }
2338     }
2339
2340     // make new volumes
2341     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2342     const int* volConn = splitMethod._connectivity;
2343     if ( splitMethod._nbCorners == 4 ) // tetra
2344       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2345         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2346                                                                nodes[ volConn[1] ],
2347                                                                nodes[ volConn[2] ],
2348                                                                nodes[ volConn[3] ]));
2349     else // prisms
2350       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2351         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2352                                                                nodes[ volConn[1] ],
2353                                                                nodes[ volConn[2] ],
2354                                                                nodes[ volConn[3] ],
2355                                                                nodes[ volConn[4] ],
2356                                                                nodes[ volConn[5] ]));
2357
2358     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2359
2360     // Split faces on sides of the split volume
2361
2362     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2363     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2364     {
2365       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2366       if ( nbNodes < 4 ) continue;
2367
2368       // find an existing face
2369       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2370                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2371       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2372                                                                        /*noMedium=*/false))
2373       {
2374         // make triangles
2375         helper.SetElementsOnShape( false );
2376         vector< const SMDS_MeshElement* > triangles;
2377
2378         // find submesh to add new triangles in
2379         if ( !fSubMesh || !fSubMesh->Contains( face ))
2380         {
2381           int shapeID = FindShape( face );
2382           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2383         }
2384         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2385         if ( iF_n != splitMethod._faceBaryNode.end() )
2386         {
2387           const SMDS_MeshNode *baryNode = iF_n->second;
2388           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2389           {
2390             const SMDS_MeshNode* n1 = fNodes[iN];
2391             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2392             const SMDS_MeshNode *n3 = baryNode;
2393             if ( !volTool.IsFaceExternal( iF ))
2394               swap( n2, n3 );
2395             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2396           }
2397           if ( fSubMesh ) // update position of the bary node on geometry
2398           {
2399             if ( subMesh )
2400               subMesh->RemoveNode( baryNode );
2401             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2402             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2403             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2404             {
2405               fHelper.SetSubShape( s );
2406               gp_XY uv( 1e100, 1e100 );
2407               double distXYZ[4];
2408               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2409                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2410                    uv.X() < 1e100 )
2411               {
2412                 // node is too far from the surface
2413                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2414                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2415                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2416               }
2417             }
2418           }
2419         }
2420         else
2421         {
2422           // among possible triangles create ones described by split method
2423           const int* nInd = volTool.GetFaceNodesIndices( iF );
2424           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2425           int iCom = 0; // common node of triangle faces to split into
2426           list< TTriangleFacet > facets;
2427           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2428           {
2429             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2430                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2431                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2432             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2433                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2434                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2435             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2436             {
2437               facets.push_back( t012 );
2438               facets.push_back( t023 );
2439               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2440                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2441                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2442                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2443               break;
2444             }
2445           }
2446           list< TTriangleFacet >::iterator facet = facets.begin();
2447           if ( facet == facets.end() )
2448             break;
2449           for ( ; facet != facets.end(); ++facet )
2450           {
2451             if ( !volTool.IsFaceExternal( iF ))
2452               swap( facet->_n2, facet->_n3 );
2453             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2454                                                  volNodes[ facet->_n2 ],
2455                                                  volNodes[ facet->_n3 ]));
2456           }
2457         }
2458         for ( size_t i = 0; i < triangles.size(); ++i )
2459         {
2460           if ( !triangles[ i ]) continue;
2461           if ( fSubMesh )
2462             fSubMesh->AddElement( triangles[ i ]);
2463           newElems.push_back( triangles[ i ]);
2464         }
2465         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2466         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2467
2468       } // while a face based on facet nodes exists
2469     } // loop on volume faces to split them into triangles
2470
2471     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2472
2473     if ( geomType == SMDSEntity_TriQuad_Hexa )
2474     {
2475       // remove medium nodes that could become free
2476       for ( int i = 20; i < volTool.NbNodes(); ++i )
2477         if ( volNodes[i]->NbInverseElements() == 0 )
2478           GetMeshDS()->RemoveNode( volNodes[i] );
2479     }
2480   } // loop on volumes to split
2481
2482   myLastCreatedNodes = newNodes;
2483   myLastCreatedElems = newElems;
2484 }
2485
2486 //=======================================================================
2487 //function : GetHexaFacetsToSplit
2488 //purpose  : For hexahedra that will be split into prisms, finds facets to
2489 //           split into triangles. Only hexahedra adjacent to the one closest
2490 //           to theFacetNormal.Location() are returned.
2491 //param [in,out] theHexas - the hexahedra
2492 //param [in]     theFacetNormal - facet normal
2493 //param [out]    theFacets - the hexahedra and found facet IDs
2494 //=======================================================================
2495
2496 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2497                                              const gp_Ax1&     theFacetNormal,
2498                                              TFacetOfElem &    theFacets)
2499 {
2500 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2501
2502   // Find a hexa closest to the location of theFacetNormal
2503
2504   const SMDS_MeshElement* startHex;
2505   {
2506     // get SMDS_ElemIteratorPtr on theHexas
2507     typedef const SMDS_MeshElement*                                      TValue;
2508     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2509     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2510     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2511     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2512     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2513       ( new TElemSetIter( theHexas.begin(),
2514                           theHexas.end(),
2515                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2516
2517     SMESH_ElementSearcher* searcher =
2518       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2519
2520     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2521
2522     delete searcher;
2523
2524     if ( !startHex )
2525       throw SALOME_Exception( THIS_METHOD "startHex not found");
2526   }
2527
2528   // Select a facet of startHex by theFacetNormal
2529
2530   SMDS_VolumeTool vTool( startHex );
2531   double norm[3], dot, maxDot = 0;
2532   int facetID = -1;
2533   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2534     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2535     {
2536       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2537       if ( dot > maxDot )
2538       {
2539         facetID = iF;
2540         maxDot = dot;
2541       }
2542     }
2543   if ( facetID < 0 )
2544     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2545
2546   // Fill theFacets starting from facetID of startHex
2547
2548   // facets used for searching of volumes adjacent to already treated ones
2549   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2550   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2551   TFacetMap facetsToCheck;
2552
2553   set<const SMDS_MeshNode*> facetNodes;
2554   const SMDS_MeshElement*   curHex;
2555
2556   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2557
2558   while ( startHex )
2559   {
2560     // move in two directions from startHex via facetID
2561     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2562     {
2563       curHex       = startHex;
2564       int curFacet = facetID;
2565       if ( is2nd ) // do not treat startHex twice
2566       {
2567         vTool.Set( curHex );
2568         if ( vTool.IsFreeFace( curFacet, &curHex ))
2569         {
2570           curHex = 0;
2571         }
2572         else
2573         {
2574           vTool.GetFaceNodes( curFacet, facetNodes );
2575           vTool.Set( curHex );
2576           curFacet = vTool.GetFaceIndex( facetNodes );
2577         }
2578       }
2579       while ( curHex )
2580       {
2581         // store a facet to split
2582         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2583         {
2584           theFacets.insert( make_pair( curHex, -1 ));
2585           break;
2586         }
2587         if ( !allHex && !theHexas.count( curHex ))
2588           break;
2589
2590         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2591           theFacets.insert( make_pair( curHex, curFacet ));
2592         if ( !facetIt2isNew.second )
2593           break;
2594
2595         // remember not-to-split facets in facetsToCheck
2596         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2597         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2598         {
2599           if ( iF == curFacet && iF == oppFacet )
2600             continue;
2601           TVolumeFaceKey facetKey ( vTool, iF );
2602           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2603           pair< TFacetMap::iterator, bool > it2isnew =
2604             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2605           if ( !it2isnew.second )
2606             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2607         }
2608         // pass to a volume adjacent via oppFacet
2609         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2610         {
2611           curHex = 0;
2612         }
2613         else
2614         {
2615           // get a new curFacet
2616           vTool.GetFaceNodes( oppFacet, facetNodes );
2617           vTool.Set( curHex );
2618           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2619         }
2620       }
2621     } // move in two directions from startHex via facetID
2622
2623     // Find a new startHex by facetsToCheck
2624
2625     startHex = 0;
2626     facetID  = -1;
2627     TFacetMap::iterator fIt = facetsToCheck.begin();
2628     while ( !startHex && fIt != facetsToCheck.end() )
2629     {
2630       const TElemFacets&  elemFacets = fIt->second;
2631       const SMDS_MeshElement*    hex = elemFacets.first->first;
2632       int                 splitFacet = elemFacets.first->second;
2633       int               lateralFacet = elemFacets.second;
2634       facetsToCheck.erase( fIt );
2635       fIt = facetsToCheck.begin();
2636
2637       vTool.Set( hex );
2638       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2639            curHex->GetGeomType() != SMDSGeom_HEXA )
2640         continue;
2641       if ( !allHex && !theHexas.count( curHex ))
2642         continue;
2643
2644       startHex = curHex;
2645
2646       // find a facet of startHex to split
2647
2648       set<const SMDS_MeshNode*> lateralNodes;
2649       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2650       vTool.GetFaceNodes( splitFacet,   facetNodes );
2651       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2652       vTool.Set( startHex );
2653       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2654
2655       // look for a facet of startHex having common nodes with facetNodes
2656       // but not lateralFacet
2657       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2658       {
2659         if ( iF == lateralFacet )
2660           continue;
2661         int nbCommonNodes = 0;
2662         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2663         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2664           nbCommonNodes += facetNodes.count( nn[ iN ]);
2665
2666         if ( nbCommonNodes >= 2 )
2667         {
2668           facetID = iF;
2669           break;
2670         }
2671       }
2672       if ( facetID < 0 )
2673         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2674     }
2675   } //   while ( startHex )
2676
2677   return;
2678 }
2679
2680 namespace
2681 {
2682   //================================================================================
2683   /*!
2684    * \brief Selects nodes of several elements according to a given interlace
2685    *  \param [in] srcNodes - nodes to select from
2686    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2687    *  \param [in] interlace - indices of nodes for all elements
2688    *  \param [in] nbElems - nb of elements
2689    *  \param [in] nbNodes - nb of nodes in each element
2690    *  \param [in] mesh - the mesh
2691    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2692    *  \param [in] type - type of elements to look for
2693    */
2694   //================================================================================
2695
2696   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2697                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2698                     const int*                            interlace,
2699                     const int                             nbElems,
2700                     const int                             nbNodes,
2701                     SMESHDS_Mesh*                         mesh = 0,
2702                     list< const SMDS_MeshElement* >*      elemQueue=0,
2703                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2704   {
2705     for ( int iE = 0; iE < nbElems; ++iE )
2706     {
2707       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2708       const int*                         select = & interlace[iE*nbNodes];
2709       elemNodes.resize( nbNodes );
2710       for ( int iN = 0; iN < nbNodes; ++iN )
2711         elemNodes[iN] = srcNodes[ select[ iN ]];
2712     }
2713     const SMDS_MeshElement* e;
2714     if ( elemQueue )
2715       for ( int iE = 0; iE < nbElems; ++iE )
2716         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2717           elemQueue->push_back( e );
2718   }
2719 }
2720
2721 //=======================================================================
2722 /*
2723  * Split bi-quadratic elements into linear ones without creation of additional nodes
2724  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2725  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2726  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2727  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2728  *   will be split in order to keep the mesh conformal.
2729  *  \param elems - elements to split
2730  */
2731 //=======================================================================
2732
2733 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2734 {
2735   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2736   vector<const SMDS_MeshElement* > splitElems;
2737   list< const SMDS_MeshElement* > elemQueue;
2738   list< const SMDS_MeshElement* >::iterator elemIt;
2739
2740   SMESHDS_Mesh * mesh = GetMeshDS();
2741   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2742   int nbElems, nbNodes;
2743
2744   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2745   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2746   {
2747     elemQueue.clear();
2748     elemQueue.push_back( *elemSetIt );
2749     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2750     {
2751       const SMDS_MeshElement* elem = *elemIt;
2752       switch( elem->GetEntityType() )
2753       {
2754       case SMDSEntity_TriQuad_Hexa: // HEX27
2755       {
2756         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2757         nbElems  = nbNodes = 8;
2758         elemType = & hexaType;
2759
2760         // get nodes for new elements
2761         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2762                                  { 1,9,20,8,    17,22,26,21 },
2763                                  { 2,10,20,9,   18,23,26,22 },
2764                                  { 3,11,20,10,  19,24,26,23 },
2765                                  { 16,21,26,24, 4,12,25,15  },
2766                                  { 17,22,26,21, 5,13,25,12  },
2767                                  { 18,23,26,22, 6,14,25,13  },
2768                                  { 19,24,26,23, 7,15,25,14  }};
2769         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2770
2771         // add boundary faces to elemQueue
2772         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2773                                  { 4,5,6,7, 12,13,14,15, 25 },
2774                                  { 0,1,5,4, 8,17,12,16,  21 },
2775                                  { 1,2,6,5, 9,18,13,17,  22 },
2776                                  { 2,3,7,6, 10,19,14,18, 23 },
2777                                  { 3,0,4,7, 11,16,15,19, 24 }};
2778         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2779
2780         // add boundary segments to elemQueue
2781         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2782                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2783                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2784         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2785         break;
2786       }
2787       case SMDSEntity_BiQuad_Triangle: // TRIA7
2788       {
2789         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2790         nbElems = 3;
2791         nbNodes = 4;
2792         elemType = & quadType;
2793
2794         // get nodes for new elements
2795         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2796         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2797
2798         // add boundary segments to elemQueue
2799         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2800         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2801         break;
2802       }
2803       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2804       {
2805         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806         nbElems = 4;
2807         nbNodes = 4;
2808         elemType = & quadType;
2809
2810         // get nodes for new elements
2811         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2812         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2813
2814         // add boundary segments to elemQueue
2815         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2816         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2817         break;
2818       }
2819       case SMDSEntity_Quad_Edge:
2820       {
2821         if ( elemIt == elemQueue.begin() )
2822           continue; // an elem is in theElems
2823         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2824         nbElems = 2;
2825         nbNodes = 2;
2826         elemType = & segType;
2827
2828         // get nodes for new elements
2829         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2830         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2831         break;
2832       }
2833       default: continue;
2834       } // switch( elem->GetEntityType() )
2835
2836       // Create new elements
2837
2838       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2839
2840       splitElems.clear();
2841
2842       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2843       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2844       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2845       //elemType->SetID( -1 );
2846
2847       for ( int iE = 0; iE < nbElems; ++iE )
2848         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2849
2850
2851       ReplaceElemInGroups( elem, splitElems, mesh );
2852
2853       if ( subMesh )
2854         for ( size_t i = 0; i < splitElems.size(); ++i )
2855           subMesh->AddElement( splitElems[i] );
2856     }
2857   }
2858 }
2859
2860 //=======================================================================
2861 //function : AddToSameGroups
2862 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2863 //=======================================================================
2864
2865 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2866                                         const SMDS_MeshElement* elemInGroups,
2867                                         SMESHDS_Mesh *          aMesh)
2868 {
2869   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870   if (!groups.empty()) {
2871     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2872     for ( ; grIt != groups.end(); grIt++ ) {
2873       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2874       if ( group && group->Contains( elemInGroups ))
2875         group->SMDSGroup().Add( elemToAdd );
2876     }
2877   }
2878 }
2879
2880
2881 //=======================================================================
2882 //function : RemoveElemFromGroups
2883 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2884 //=======================================================================
2885 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2886                                              SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty())
2890   {
2891     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2892     for (; GrIt != groups.end(); GrIt++)
2893     {
2894       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2895       if (!grp || grp->IsEmpty()) continue;
2896       grp->SMDSGroup().Remove(removeelem);
2897     }
2898   }
2899 }
2900
2901 //================================================================================
2902 /*!
2903  * \brief Replace elemToRm by elemToAdd in the all groups
2904  */
2905 //================================================================================
2906
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908                                             const SMDS_MeshElement* elemToAdd,
2909                                             SMESHDS_Mesh *          aMesh)
2910 {
2911   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912   if (!groups.empty()) {
2913     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914     for ( ; grIt != groups.end(); grIt++ ) {
2915       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2917         group->SMDSGroup().Add( elemToAdd );
2918     }
2919   }
2920 }
2921
2922 //================================================================================
2923 /*!
2924  * \brief Replace elemToRm by elemToAdd in the all groups
2925  */
2926 //================================================================================
2927
2928 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2929                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2930                                             SMESHDS_Mesh *                         aMesh)
2931 {
2932   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2933   if (!groups.empty())
2934   {
2935     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2936     for ( ; grIt != groups.end(); grIt++ ) {
2937       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2938       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2939         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2940           group->SMDSGroup().Add( elemToAdd[ i ] );
2941     }
2942   }
2943 }
2944
2945 //=======================================================================
2946 //function : QuadToTri
2947 //purpose  : Cut quadrangles into triangles.
2948 //           theCrit is used to select a diagonal to cut
2949 //=======================================================================
2950
2951 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2952                                   const bool         the13Diag)
2953 {
2954   ClearLastCreated();
2955   myLastCreatedElems.reserve( theElems.size() * 2 );
2956
2957   SMESHDS_Mesh *       aMesh = GetMeshDS();
2958   Handle(Geom_Surface) surface;
2959   SMESH_MesherHelper   helper( *GetMesh() );
2960
2961   TIDSortedElemSet::iterator itElem;
2962   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2963   {
2964     const SMDS_MeshElement* elem = *itElem;
2965     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2966       continue;
2967
2968     if ( elem->NbNodes() == 4 ) {
2969       // retrieve element nodes
2970       const SMDS_MeshNode* aNodes [4];
2971       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2972       int i = 0;
2973       while ( itN->more() )
2974         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2975
2976       int aShapeId = FindShape( elem );
2977       const SMDS_MeshElement* newElem1 = 0;
2978       const SMDS_MeshElement* newElem2 = 0;
2979       if ( the13Diag ) {
2980         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2981         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2982       }
2983       else {
2984         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2985         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2986       }
2987       myLastCreatedElems.push_back(newElem1);
2988       myLastCreatedElems.push_back(newElem2);
2989       // put a new triangle on the same shape and add to the same groups
2990       if ( aShapeId )
2991       {
2992         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2993         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2994       }
2995       AddToSameGroups( newElem1, elem, aMesh );
2996       AddToSameGroups( newElem2, elem, aMesh );
2997       aMesh->RemoveElement( elem );
2998     }
2999
3000     // Quadratic quadrangle
3001
3002     else if ( elem->NbNodes() >= 8 )
3003     {
3004       // get surface elem is on
3005       int aShapeId = FindShape( elem );
3006       if ( aShapeId != helper.GetSubShapeID() ) {
3007         surface.Nullify();
3008         TopoDS_Shape shape;
3009         if ( aShapeId > 0 )
3010           shape = aMesh->IndexToShape( aShapeId );
3011         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3012           TopoDS_Face face = TopoDS::Face( shape );
3013           surface = BRep_Tool::Surface( face );
3014           if ( !surface.IsNull() )
3015             helper.SetSubShape( shape );
3016         }
3017       }
3018
3019       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3020       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3021       for ( int i = 0; itN->more(); ++i )
3022         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3023
3024       const SMDS_MeshNode* centrNode = aNodes[8];
3025       if ( centrNode == 0 )
3026       {
3027         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3028                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3029                                            surface.IsNull() );
3030         myLastCreatedNodes.push_back(centrNode);
3031       }
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], centrNode );
3039         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040                                   centrNode, aNodes[4], aNodes[5] );
3041       }
3042       else {
3043         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044                                   aNodes[7], aNodes[4], centrNode );
3045         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046                                   centrNode, aNodes[5], aNodes[6] );
3047       }
3048       myLastCreatedElems.push_back(newElem1);
3049       myLastCreatedElems.push_back(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   smIdType GetLinkID (const SMDS_MeshNode * n1,
3138                   const SMDS_MeshNode * n2) const
3139   {
3140     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::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   ClearLastCreated();
3174   myLastCreatedElems.reserve( theElems.size() / 2 );
3175
3176   if ( !theCrit.get() )
3177     return false;
3178
3179   SMESHDS_Mesh * aMesh = GetMeshDS();
3180
3181   // Prepare data for algo: build
3182   // 1. map of elements with their linkIDs
3183   // 2. map of linkIDs with their elements
3184
3185   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3188   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3189
3190   TIDSortedElemSet::iterator itElem;
3191   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3192   {
3193     const SMDS_MeshElement* elem = *itElem;
3194     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195     bool IsTria = ( elem->NbCornerNodes()==3 );
3196     if (!IsTria) continue;
3197
3198     // retrieve element nodes
3199     const SMDS_MeshNode* aNodes [4];
3200     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3201     int i = 0;
3202     while ( i < 3 )
3203       aNodes[ i++ ] = itN->next();
3204     aNodes[ 3 ] = aNodes[ 0 ];
3205
3206     // fill maps
3207     for ( i = 0; i < 3; i++ ) {
3208       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209       // check if elements sharing a link can be fused
3210       itLE = mapLi_listEl.find( link );
3211       if ( itLE != mapLi_listEl.end() ) {
3212         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3213           continue;
3214         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215         //if ( FindShape( elem ) != FindShape( elem2 ))
3216         //  continue; // do not fuse triangles laying on different shapes
3217         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218           continue; // avoid making badly shaped quads
3219         (*itLE).second.push_back( elem );
3220       }
3221       else {
3222         mapLi_listEl[ link ].push_back( elem );
3223       }
3224       mapEl_setLi [ elem ].insert( link );
3225     }
3226   }
3227   // Clean the maps from the links shared by a sole element, ie
3228   // links to which only one element is bound in mapLi_listEl
3229
3230   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231     int nbElems = (*itLE).second.size();
3232     if ( nbElems < 2  ) {
3233       const SMDS_MeshElement* elem = (*itLE).second.front();
3234       SMESH_TLink link = (*itLE).first;
3235       mapEl_setLi[ elem ].erase( link );
3236       if ( mapEl_setLi[ elem ].empty() )
3237         mapEl_setLi.erase( elem );
3238     }
3239   }
3240
3241   // Algo: fuse triangles into quadrangles
3242
3243   while ( ! mapEl_setLi.empty() ) {
3244     // Look for the start element:
3245     // the element having the least nb of shared links
3246     const SMDS_MeshElement* startElem = 0;
3247     int minNbLinks = 4;
3248     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249       int nbLinks = (*itEL).second.size();
3250       if ( nbLinks < minNbLinks ) {
3251         startElem = (*itEL).first;
3252         minNbLinks = nbLinks;
3253         if ( minNbLinks == 1 )
3254           break;
3255       }
3256     }
3257
3258     // search elements to fuse starting from startElem or links of elements
3259     // fused earlyer - startLinks
3260     list< SMESH_TLink > startLinks;
3261     while ( startElem || !startLinks.empty() ) {
3262       while ( !startElem && !startLinks.empty() ) {
3263         // Get an element to start, by a link
3264         SMESH_TLink linkId = startLinks.front();
3265         startLinks.pop_front();
3266         itLE = mapLi_listEl.find( linkId );
3267         if ( itLE != mapLi_listEl.end() ) {
3268           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270           for ( ; itE != listElem.end() ; itE++ )
3271             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3272               startElem = (*itE);
3273           mapLi_listEl.erase( itLE );
3274         }
3275       }
3276
3277       if ( startElem ) {
3278         // Get candidates to be fused
3279         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280         const SMESH_TLink *link12 = 0, *link13 = 0;
3281         startElem = 0;
3282         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284         ASSERT( !setLi.empty() );
3285         set< SMESH_TLink >::iterator itLi;
3286         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3287         {
3288           const SMESH_TLink & link = (*itLi);
3289           itLE = mapLi_listEl.find( link );
3290           if ( itLE == mapLi_listEl.end() )
3291             continue;
3292
3293           const SMDS_MeshElement* elem = (*itLE).second.front();
3294           if ( elem == tr1 )
3295             elem = (*itLE).second.back();
3296           mapLi_listEl.erase( itLE );
3297           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3298             continue;
3299           if ( tr2 ) {
3300             tr3 = elem;
3301             link13 = &link;
3302           }
3303           else {
3304             tr2 = elem;
3305             link12 = &link;
3306           }
3307
3308           // add other links of elem to list of links to re-start from
3309           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310           set< SMESH_TLink >::iterator it;
3311           for ( it = links.begin(); it != links.end(); it++ ) {
3312             const SMESH_TLink& link2 = (*it);
3313             if ( link2 != link )
3314               startLinks.push_back( link2 );
3315           }
3316         }
3317
3318         // Get nodes of possible quadrangles
3319         const SMDS_MeshNode *n12 [4], *n13 [4];
3320         bool Ok12 = false, Ok13 = false;
3321         const SMDS_MeshNode *linkNode1, *linkNode2;
3322         if(tr2) {
3323           linkNode1 = link12->first;
3324           linkNode2 = link12->second;
3325           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3326             Ok12 = true;
3327         }
3328         if(tr3) {
3329           linkNode1 = link13->first;
3330           linkNode2 = link13->second;
3331           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3332             Ok13 = true;
3333         }
3334
3335         // Choose a pair to fuse
3336         if ( Ok12 && Ok13 ) {
3337           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339           double aBadRate12 = getBadRate( &quad12, theCrit );
3340           double aBadRate13 = getBadRate( &quad13, theCrit );
3341           if (  aBadRate13 < aBadRate12 )
3342             Ok12 = false;
3343           else
3344             Ok13 = false;
3345         }
3346
3347         // Make quadrangles
3348         // and remove fused elems and remove links from the maps
3349         mapEl_setLi.erase( tr1 );
3350         if ( Ok12 )
3351         {
3352           mapEl_setLi.erase( tr2 );
3353           mapLi_listEl.erase( *link12 );
3354           if ( tr1->NbNodes() == 3 )
3355           {
3356             const SMDS_MeshElement* newElem = 0;
3357             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358             myLastCreatedElems.push_back(newElem);
3359             AddToSameGroups( newElem, tr1, aMesh );
3360             int aShapeId = tr1->getshapeId();
3361             if ( aShapeId )
3362               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363             aMesh->RemoveElement( tr1 );
3364             aMesh->RemoveElement( tr2 );
3365           }
3366           else {
3367             vector< const SMDS_MeshNode* > N1;
3368             vector< const SMDS_MeshNode* > N2;
3369             getNodesFromTwoTria(tr1,tr2,N1,N2);
3370             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3372             // i.e. first nodes from both arrays form a new diagonal
3373             const SMDS_MeshNode* aNodes[8];
3374             aNodes[0] = N1[0];
3375             aNodes[1] = N1[1];
3376             aNodes[2] = N2[0];
3377             aNodes[3] = N2[1];
3378             aNodes[4] = N1[3];
3379             aNodes[5] = N2[5];
3380             aNodes[6] = N2[3];
3381             aNodes[7] = N1[5];
3382             const SMDS_MeshElement* newElem = 0;
3383             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3386             else
3387               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389             myLastCreatedElems.push_back(newElem);
3390             AddToSameGroups( newElem, tr1, aMesh );
3391             int aShapeId = tr1->getshapeId();
3392             if ( aShapeId )
3393               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394             aMesh->RemoveElement( tr1 );
3395             aMesh->RemoveElement( tr2 );
3396             // remove middle node (9)
3397             if ( N1[4]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N1[4] );
3399             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400               aMesh->RemoveNode( N1[6] );
3401             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402               aMesh->RemoveNode( N2[6] );
3403           }
3404         }
3405         else if ( Ok13 )
3406         {
3407           mapEl_setLi.erase( tr3 );
3408           mapLi_listEl.erase( *link13 );
3409           if ( tr1->NbNodes() == 3 ) {
3410             const SMDS_MeshElement* newElem = 0;
3411             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412             myLastCreatedElems.push_back(newElem);
3413             AddToSameGroups( newElem, tr1, aMesh );
3414             int aShapeId = tr1->getshapeId();
3415             if ( aShapeId )
3416               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417             aMesh->RemoveElement( tr1 );
3418             aMesh->RemoveElement( tr3 );
3419           }
3420           else {
3421             vector< const SMDS_MeshNode* > N1;
3422             vector< const SMDS_MeshNode* > N2;
3423             getNodesFromTwoTria(tr1,tr3,N1,N2);
3424             // now we receive following N1 and N2 (using numeration as above image)
3425             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3426             // i.e. first nodes from both arrays form a new diagonal
3427             const SMDS_MeshNode* aNodes[8];
3428             aNodes[0] = N1[0];
3429             aNodes[1] = N1[1];
3430             aNodes[2] = N2[0];
3431             aNodes[3] = N2[1];
3432             aNodes[4] = N1[3];
3433             aNodes[5] = N2[5];
3434             aNodes[6] = N2[3];
3435             aNodes[7] = N1[5];
3436             const SMDS_MeshElement* newElem = 0;
3437             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3440             else
3441               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443             myLastCreatedElems.push_back(newElem);
3444             AddToSameGroups( newElem, tr1, aMesh );
3445             int aShapeId = tr1->getshapeId();
3446             if ( aShapeId )
3447               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448             aMesh->RemoveElement( tr1 );
3449             aMesh->RemoveElement( tr3 );
3450             // remove middle node (9)
3451             if ( N1[4]->NbInverseElements() == 0 )
3452               aMesh->RemoveNode( N1[4] );
3453             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454               aMesh->RemoveNode( N1[6] );
3455             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456               aMesh->RemoveNode( N2[6] );
3457           }
3458         }
3459
3460         // Next element to fuse: the rejected one
3461         if ( tr3 )
3462           startElem = Ok12 ? tr3 : tr2;
3463
3464       } // if ( startElem )
3465     } // while ( startElem || !startLinks.empty() )
3466   } // while ( ! mapEl_setLi.empty() )
3467
3468   return true;
3469 }
3470
3471 //================================================================================
3472 /*!
3473  * \brief Return nodes linked to the given one
3474  * \param theNode - the node
3475  * \param linkedNodes - the found nodes
3476  * \param type - the type of elements to check
3477  *
3478  * Medium nodes are ignored
3479  */
3480 //================================================================================
3481
3482 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3483                                        TIDSortedElemSet &   linkedNodes,
3484                                        SMDSAbs_ElementType  type )
3485 {
3486   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3487   while ( elemIt->more() )
3488   {
3489     const SMDS_MeshElement* elem = elemIt->next();
3490     if(elem->GetType() == SMDSAbs_0DElement)
3491       continue;
3492
3493     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3494     if ( elem->GetType() == SMDSAbs_Volume )
3495     {
3496       SMDS_VolumeTool vol( elem );
3497       while ( nodeIt->more() ) {
3498         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3499         if ( theNode != n && vol.IsLinked( theNode, n ))
3500           linkedNodes.insert( n );
3501       }
3502     }
3503     else
3504     {
3505       for ( int i = 0; nodeIt->more(); ++i ) {
3506         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3507         if ( n == theNode ) {
3508           int iBefore = i - 1;
3509           int iAfter  = i + 1;
3510           if ( elem->IsQuadratic() ) {
3511             int nb = elem->NbNodes() / 2;
3512             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3513             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3514           }
3515           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3516           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3517         }
3518       }
3519     }
3520   }
3521 }
3522
3523 //=======================================================================
3524 //function : laplacianSmooth
3525 //purpose  : pulls theNode toward the center of surrounding nodes directly
3526 //           connected to that node along an element edge
3527 //=======================================================================
3528
3529 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3530                      const Handle(Geom_Surface)&          theSurface,
3531                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3532 {
3533   // find surrounding nodes
3534
3535   TIDSortedElemSet nodeSet;
3536   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3537
3538   // compute new coodrs
3539
3540   double coord[] = { 0., 0., 0. };
3541   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3542   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3543     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3544     if ( theSurface.IsNull() ) { // smooth in 3D
3545       coord[0] += node->X();
3546       coord[1] += node->Y();
3547       coord[2] += node->Z();
3548     }
3549     else { // smooth in 2D
3550       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3551       gp_XY* uv = theUVMap[ node ];
3552       coord[0] += uv->X();
3553       coord[1] += uv->Y();
3554     }
3555   }
3556   int nbNodes = nodeSet.size();
3557   if ( !nbNodes )
3558     return;
3559   coord[0] /= nbNodes;
3560   coord[1] /= nbNodes;
3561
3562   if ( !theSurface.IsNull() ) {
3563     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3564     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3565     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3566     coord[0] = p3d.X();
3567     coord[1] = p3d.Y();
3568     coord[2] = p3d.Z();
3569   }
3570   else
3571     coord[2] /= nbNodes;
3572
3573   // move node
3574
3575   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3576 }
3577
3578 //=======================================================================
3579 //function : centroidalSmooth
3580 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3581 //           surrounding elements
3582 //=======================================================================
3583
3584 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3585                       const Handle(Geom_Surface)&          theSurface,
3586                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3587 {
3588   gp_XYZ aNewXYZ(0.,0.,0.);
3589   SMESH::Controls::Area anAreaFunc;
3590   double totalArea = 0.;
3591   int nbElems = 0;
3592
3593   // compute new XYZ
3594
3595   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3596   while ( elemIt->more() )
3597   {
3598     const SMDS_MeshElement* elem = elemIt->next();
3599     nbElems++;
3600
3601     gp_XYZ elemCenter(0.,0.,0.);
3602     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3603     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3604     int nn = elem->NbNodes();
3605     if(elem->IsQuadratic()) nn = nn/2;
3606     int i=0;
3607     //while ( itN->more() ) {
3608     while ( i<nn ) {
3609       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3610       i++;
3611       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3612       aNodePoints.push_back( aP );
3613       if ( !theSurface.IsNull() ) { // smooth in 2D
3614         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3615         gp_XY* uv = theUVMap[ aNode ];
3616         aP.SetCoord( uv->X(), uv->Y(), 0. );
3617       }
3618       elemCenter += aP;
3619     }
3620     double elemArea = anAreaFunc.GetValue( aNodePoints );
3621     totalArea += elemArea;
3622     elemCenter /= nn;
3623     aNewXYZ += elemCenter * elemArea;
3624   }
3625   aNewXYZ /= totalArea;
3626   if ( !theSurface.IsNull() ) {
3627     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3628     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3629   }
3630
3631   // move node
3632
3633   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3634 }
3635
3636 //=======================================================================
3637 //function : getClosestUV
3638 //purpose  : return UV of closest projection
3639 //=======================================================================
3640
3641 static bool getClosestUV (Extrema_GenExtPS& projector,
3642                           const gp_Pnt&     point,
3643                           gp_XY &           result)
3644 {
3645   projector.Perform( point );
3646   if ( projector.IsDone() ) {
3647     double u = 0, v = 0, minVal = DBL_MAX;
3648     for ( int i = projector.NbExt(); i > 0; i-- )
3649       if ( projector.SquareDistance( i ) < minVal ) {
3650         minVal = projector.SquareDistance( i );
3651         projector.Point( i ).Parameter( u, v );
3652       }
3653     result.SetCoord( u, v );
3654     return true;
3655   }
3656   return false;
3657 }
3658
3659 //=======================================================================
3660 //function : Smooth
3661 //purpose  : Smooth theElements during theNbIterations or until a worst
3662 //           element has aspect ratio <= theTgtAspectRatio.
3663 //           Aspect Ratio varies in range [1.0, inf].
3664 //           If theElements is empty, the whole mesh is smoothed.
3665 //           theFixedNodes contains additionally fixed nodes. Nodes built
3666 //           on edges and boundary nodes are always fixed.
3667 //=======================================================================
3668
3669 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3670                                set<const SMDS_MeshNode*> & theFixedNodes,
3671                                const SmoothMethod          theSmoothMethod,
3672                                const int                   theNbIterations,
3673                                double                      theTgtAspectRatio,
3674                                const bool                  the2D)
3675 {
3676   ClearLastCreated();
3677
3678   if ( theTgtAspectRatio < 1.0 )
3679     theTgtAspectRatio = 1.0;
3680
3681   const double disttol = 1.e-16;
3682
3683   SMESH::Controls::AspectRatio aQualityFunc;
3684
3685   SMESHDS_Mesh* aMesh = GetMeshDS();
3686
3687   if ( theElems.empty() ) {
3688     // add all faces to theElems
3689     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3690     while ( fIt->more() ) {
3691       const SMDS_MeshElement* face = fIt->next();
3692       theElems.insert( theElems.end(), face );
3693     }
3694   }
3695   // get all face ids theElems are on
3696   set< int > faceIdSet;
3697   TIDSortedElemSet::iterator itElem;
3698   if ( the2D )
3699     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3700       int fId = FindShape( *itElem );
3701       // check that corresponding submesh exists and a shape is face
3702       if (fId &&
3703           faceIdSet.find( fId ) == faceIdSet.end() &&
3704           aMesh->MeshElements( fId )) {
3705         TopoDS_Shape F = aMesh->IndexToShape( fId );
3706         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3707           faceIdSet.insert( fId );
3708       }
3709     }
3710   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3711
3712   // ===============================================
3713   // smooth elements on each TopoDS_Face separately
3714   // ===============================================
3715
3716   SMESH_MesherHelper helper( *GetMesh() );
3717
3718   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3719   for ( ; fId != faceIdSet.rend(); ++fId )
3720   {
3721     // get face surface and submesh
3722     Handle(Geom_Surface) surface;
3723     SMESHDS_SubMesh* faceSubMesh = 0;
3724     TopoDS_Face face;
3725     double fToler2 = 0;
3726     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3727     bool isUPeriodic = false, isVPeriodic = false;
3728     if ( *fId )
3729     {
3730       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3731       surface = BRep_Tool::Surface( face );
3732       faceSubMesh = aMesh->MeshElements( *fId );
3733       fToler2 = BRep_Tool::Tolerance( face );
3734       fToler2 *= fToler2 * 10.;
3735       isUPeriodic = surface->IsUPeriodic();
3736       // if ( isUPeriodic )
3737       //   surface->UPeriod();
3738       isVPeriodic = surface->IsVPeriodic();
3739       // if ( isVPeriodic )
3740       //   surface->VPeriod();
3741       surface->Bounds( u1, u2, v1, v2 );
3742       helper.SetSubShape( face );
3743     }
3744     // ---------------------------------------------------------
3745     // for elements on a face, find movable and fixed nodes and
3746     // compute UV for them
3747     // ---------------------------------------------------------
3748     bool checkBoundaryNodes = false;
3749     bool isQuadratic = false;
3750     set<const SMDS_MeshNode*> setMovableNodes;
3751     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3752     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3753     list< const SMDS_MeshElement* > elemsOnFace;
3754
3755     Extrema_GenExtPS projector;
3756     GeomAdaptor_Surface surfAdaptor;
3757     if ( !surface.IsNull() ) {
3758       surfAdaptor.Load( surface );
3759       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3760     }
3761     int nbElemOnFace = 0;
3762     itElem = theElems.begin();
3763     // loop on not yet smoothed elements: look for elems on a face
3764     while ( itElem != theElems.end() )
3765     {
3766       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3767         break; // all elements found
3768
3769       const SMDS_MeshElement* elem = *itElem;
3770       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3771            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3772         ++itElem;
3773         continue;
3774       }
3775       elemsOnFace.push_back( elem );
3776       theElems.erase( itElem++ );
3777       nbElemOnFace++;
3778
3779       if ( !isQuadratic )
3780         isQuadratic = elem->IsQuadratic();
3781
3782       // get movable nodes of elem
3783       const SMDS_MeshNode* node;
3784       SMDS_TypeOfPosition posType;
3785       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3786       int nn = 0, nbn =  elem->NbNodes();
3787       if(elem->IsQuadratic())
3788         nbn = nbn/2;
3789       while ( nn++ < nbn ) {
3790         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3791         const SMDS_PositionPtr& pos = node->GetPosition();
3792         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3793         if (posType != SMDS_TOP_EDGE &&
3794             posType != SMDS_TOP_VERTEX &&
3795             theFixedNodes.find( node ) == theFixedNodes.end())
3796         {
3797           // check if all faces around the node are on faceSubMesh
3798           // because a node on edge may be bound to face
3799           bool all = true;
3800           if ( faceSubMesh ) {
3801             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802             while ( eIt->more() && all ) {
3803               const SMDS_MeshElement* e = eIt->next();
3804               all = faceSubMesh->Contains( e );
3805             }
3806           }
3807           if ( all )
3808             setMovableNodes.insert( node );
3809           else
3810             checkBoundaryNodes = true;
3811         }
3812         if ( posType == SMDS_TOP_3DSPACE )
3813           checkBoundaryNodes = true;
3814       }
3815
3816       if ( surface.IsNull() )
3817         continue;
3818
3819       // get nodes to check UV
3820       list< const SMDS_MeshNode* > uvCheckNodes;
3821       const SMDS_MeshNode* nodeInFace = 0;
3822       itN = elem->nodesIterator();
3823       nn = 0; nbn =  elem->NbNodes();
3824       if(elem->IsQuadratic())
3825         nbn = nbn/2;
3826       while ( nn++ < nbn ) {
3827         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3828         if ( node->GetPosition()->GetDim() == 2 )
3829           nodeInFace = node;
3830         if ( uvMap.find( node ) == uvMap.end() )
3831           uvCheckNodes.push_back( node );
3832         // add nodes of elems sharing node
3833         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3834         //         while ( eIt->more() ) {
3835         //           const SMDS_MeshElement* e = eIt->next();
3836         //           if ( e != elem ) {
3837         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3838         //             while ( nIt->more() ) {
3839         //               const SMDS_MeshNode* n =
3840         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3841         //               if ( uvMap.find( n ) == uvMap.end() )
3842         //                 uvCheckNodes.push_back( n );
3843         //             }
3844         //           }
3845         //         }
3846       }
3847       // check UV on face
3848       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3849       for ( ; n != uvCheckNodes.end(); ++n ) {
3850         node = *n;
3851         gp_XY uv( 0, 0 );
3852         const SMDS_PositionPtr& pos = node->GetPosition();
3853         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3854         // get existing UV
3855         if ( pos )
3856         {
3857           bool toCheck = true;
3858           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3859         }
3860         // compute not existing UV
3861         bool project = ( posType == SMDS_TOP_3DSPACE );
3862         // double dist1 = DBL_MAX, dist2 = 0;
3863         // if ( posType != SMDS_TOP_3DSPACE ) {
3864         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3865         //   project = dist1 > fToler2;
3866         // }
3867         if ( project ) { // compute new UV
3868           gp_XY newUV;
3869           gp_Pnt pNode = SMESH_NodeXYZ( node );
3870           if ( !getClosestUV( projector, pNode, newUV )) {
3871             MESSAGE("Node Projection Failed " << node);
3872           }
3873           else {
3874             if ( isUPeriodic )
3875               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3876             if ( isVPeriodic )
3877               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3878             // check new UV
3879             // if ( posType != SMDS_TOP_3DSPACE )
3880             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3881             // if ( dist2 < dist1 )
3882             uv = newUV;
3883           }
3884         }
3885         // store UV in the map
3886         listUV.push_back( uv );
3887         uvMap.insert( make_pair( node, &listUV.back() ));
3888       }
3889     } // loop on not yet smoothed elements
3890
3891     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3892       checkBoundaryNodes = true;
3893
3894     // fix nodes on mesh boundary
3895
3896     if ( checkBoundaryNodes ) {
3897       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3898       map< SMESH_TLink, int >::iterator link_nb;
3899       // put all elements links to linkNbMap
3900       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3901       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3902         const SMDS_MeshElement* elem = (*elemIt);
3903         int nbn =  elem->NbCornerNodes();
3904         // loop on elem links: insert them in linkNbMap
3905         for ( int iN = 0; iN < nbn; ++iN ) {
3906           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3907           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3908           SMESH_TLink link( n1, n2 );
3909           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3910           link_nb->second++;
3911         }
3912       }
3913       // remove nodes that are in links encountered only once from setMovableNodes
3914       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3915         if ( link_nb->second == 1 ) {
3916           setMovableNodes.erase( link_nb->first.node1() );
3917           setMovableNodes.erase( link_nb->first.node2() );
3918         }
3919       }
3920     }
3921
3922     // -----------------------------------------------------
3923     // for nodes on seam edge, compute one more UV ( uvMap2 );
3924     // find movable nodes linked to nodes on seam and which
3925     // are to be smoothed using the second UV ( uvMap2 )
3926     // -----------------------------------------------------
3927
3928     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3929     if ( !surface.IsNull() ) {
3930       TopExp_Explorer eExp( face, TopAbs_EDGE );
3931       for ( ; eExp.More(); eExp.Next() ) {
3932         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3933         if ( !BRep_Tool::IsClosed( edge, face ))
3934           continue;
3935         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3936         if ( !sm ) continue;
3937         // find out which parameter varies for a node on seam
3938         double f,l;
3939         gp_Pnt2d uv1, uv2;
3940         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941         if ( pcurve.IsNull() ) continue;
3942         uv1 = pcurve->Value( f );
3943         edge.Reverse();
3944         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3945         if ( pcurve.IsNull() ) continue;
3946         uv2 = pcurve->Value( f );
3947         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3948         // assure uv1 < uv2
3949         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3950           std::swap( uv1, uv2 );
3951         // get nodes on seam and its vertices
3952         list< const SMDS_MeshNode* > seamNodes;
3953         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3954         while ( nSeamIt->more() ) {
3955           const SMDS_MeshNode* node = nSeamIt->next();
3956           if ( !isQuadratic || !IsMedium( node ))
3957             seamNodes.push_back( node );
3958         }
3959         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3960         for ( ; vExp.More(); vExp.Next() ) {
3961           sm = aMesh->MeshElements( vExp.Current() );
3962           if ( sm ) {
3963             nSeamIt = sm->GetNodes();
3964             while ( nSeamIt->more() )
3965               seamNodes.push_back( nSeamIt->next() );
3966           }
3967         }
3968         // loop on nodes on seam
3969         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3970         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3971           const SMDS_MeshNode* nSeam = *noSeIt;
3972           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3973           if ( n_uv == uvMap.end() )
3974             continue;
3975           // set the first UV
3976           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3977           // set the second UV
3978           listUV.push_back( *n_uv->second );
3979           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3980           if ( uvMap2.empty() )
3981             uvMap2 = uvMap; // copy the uvMap contents
3982           uvMap2[ nSeam ] = &listUV.back();
3983
3984           // collect movable nodes linked to ones on seam in nodesNearSeam
3985           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3986           while ( eIt->more() ) {
3987             const SMDS_MeshElement* e = eIt->next();
3988             int nbUseMap1 = 0, nbUseMap2 = 0;
3989             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3990             int nn = 0, nbn =  e->NbNodes();
3991             if(e->IsQuadratic()) nbn = nbn/2;
3992             while ( nn++ < nbn )
3993             {
3994               const SMDS_MeshNode* n =
3995                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3996               if (n == nSeam ||
3997                   setMovableNodes.find( n ) == setMovableNodes.end() )
3998                 continue;
3999               // add only nodes being closer to uv2 than to uv1
4000               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4001               //              0.5 * ( n->Y() + nSeam->Y() ),
4002               //              0.5 * ( n->Z() + nSeam->Z() ));
4003               // gp_XY uv;
4004               // getClosestUV( projector, pMid, uv );
4005               double x = uvMap[ n ]->Coord( iPar );
4006               if ( Abs( uv1.Coord( iPar ) - x ) >
4007                    Abs( uv2.Coord( iPar ) - x )) {
4008                 nodesNearSeam.insert( n );
4009                 nbUseMap2++;
4010               }
4011               else
4012                 nbUseMap1++;
4013             }
4014             // for centroidalSmooth all element nodes must
4015             // be on one side of a seam
4016             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4017               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4018               nn = 0;
4019               while ( nn++ < nbn ) {
4020                 const SMDS_MeshNode* n =
4021                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4022                 setMovableNodes.erase( n );
4023               }
4024             }
4025           }
4026         } // loop on nodes on seam
4027       } // loop on edge of a face
4028     } // if ( !face.IsNull() )
4029
4030     if ( setMovableNodes.empty() ) {
4031       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4032       continue; // goto next face
4033     }
4034
4035     // -------------
4036     // SMOOTHING //
4037     // -------------
4038
4039     int it = -1;
4040     double maxRatio = -1., maxDisplacement = -1.;
4041     set<const SMDS_MeshNode*>::iterator nodeToMove;
4042     for ( it = 0; it < theNbIterations; it++ ) {
4043       maxDisplacement = 0.;
4044       nodeToMove = setMovableNodes.begin();
4045       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4046         const SMDS_MeshNode* node = (*nodeToMove);
4047         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4048
4049         // smooth
4050         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4051         if ( theSmoothMethod == LAPLACIAN )
4052           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4053         else
4054           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4055
4056         // node displacement
4057         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4058         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4059         if ( aDispl > maxDisplacement )
4060           maxDisplacement = aDispl;
4061       }
4062       // no node movement => exit
4063       //if ( maxDisplacement < 1.e-16 ) {
4064       if ( maxDisplacement < disttol ) {
4065         MESSAGE("-- no node movement --");
4066         break;
4067       }
4068
4069       // check elements quality
4070       maxRatio  = 0;
4071       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4072       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4073         const SMDS_MeshElement* elem = (*elemIt);
4074         if ( !elem || elem->GetType() != SMDSAbs_Face )
4075           continue;
4076         SMESH::Controls::TSequenceOfXYZ aPoints;
4077         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4078           double aValue = aQualityFunc.GetValue( aPoints );
4079           if ( aValue > maxRatio )
4080             maxRatio = aValue;
4081         }
4082       }
4083       if ( maxRatio <= theTgtAspectRatio ) {
4084         //MESSAGE("-- quality achieved --");
4085         break;
4086       }
4087       if (it+1 == theNbIterations) {
4088         //MESSAGE("-- Iteration limit exceeded --");
4089       }
4090     } // smoothing iterations
4091
4092     // MESSAGE(" Face id: " << *fId <<
4093     //         " Nb iterstions: " << it <<
4094     //         " Displacement: " << maxDisplacement <<
4095     //         " Aspect Ratio " << maxRatio);
4096
4097     // ---------------------------------------
4098     // new nodes positions are computed,
4099     // record movement in DS and set new UV
4100     // ---------------------------------------
4101     nodeToMove = setMovableNodes.begin();
4102     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4103       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4104       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4105       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4106       if ( node_uv != uvMap.end() ) {
4107         gp_XY* uv = node_uv->second;
4108         node->SetPosition
4109           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4110       }
4111     }
4112
4113     // move medium nodes of quadratic elements
4114     if ( isQuadratic )
4115     {
4116       vector<const SMDS_MeshNode*> nodes;
4117       bool checkUV;
4118       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4119       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4120       {
4121         const SMDS_MeshElement* QF = *elemIt;
4122         if ( QF->IsQuadratic() )
4123         {
4124           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4125                         SMDS_MeshElement::iterator() );
4126           nodes.push_back( nodes[0] );
4127           gp_Pnt xyz;
4128           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4129           {
4130             if ( !surface.IsNull() )
4131             {
4132               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4133               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4134               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4135               xyz = surface->Value( uv.X(), uv.Y() );
4136             }
4137             else {
4138               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4139             }
4140             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4141               // we have to move a medium node
4142               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4143           }
4144         }
4145       }
4146     }
4147
4148   } // loop on face ids
4149
4150 }
4151
4152 namespace
4153 {
4154   //=======================================================================
4155   //function : isReverse
4156   //purpose  : Return true if normal of prevNodes is not co-directied with
4157   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4158   //           iNotSame is where prevNodes and nextNodes are different.
4159   //           If result is true then future volume orientation is OK
4160   //=======================================================================
4161
4162   bool isReverse(const SMDS_MeshElement*             face,
4163                  const vector<const SMDS_MeshNode*>& prevNodes,
4164                  const vector<const SMDS_MeshNode*>& nextNodes,
4165                  const int                           iNotSame)
4166   {
4167
4168     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4169     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4170     gp_XYZ extrDir( pN - pP ), faceNorm;
4171     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4172
4173     return faceNorm * extrDir < 0.0;
4174   }
4175
4176   //================================================================================
4177   /*!
4178    * \brief Assure that theElemSets[0] holds elements, not nodes
4179    */
4180   //================================================================================
4181
4182   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4183   {
4184     if ( !theElemSets[0].empty() &&
4185          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4186     {
4187       std::swap( theElemSets[0], theElemSets[1] );
4188     }
4189     else if ( !theElemSets[1].empty() &&
4190               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4191     {
4192       std::swap( theElemSets[0], theElemSets[1] );
4193     }
4194   }
4195 }
4196
4197 //=======================================================================
4198 /*!
4199  * \brief Create elements by sweeping an element
4200  * \param elem - element to sweep
4201  * \param newNodesItVec - nodes generated from each node of the element
4202  * \param newElems - generated elements
4203  * \param nbSteps - number of sweeping steps
4204  * \param srcElements - to append elem for each generated element
4205  */
4206 //=======================================================================
4207
4208 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4209                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4210                                     list<const SMDS_MeshElement*>&        newElems,
4211                                     const size_t                          nbSteps,
4212                                     SMESH_SequenceOfElemPtr&              srcElements)
4213 {
4214   SMESHDS_Mesh* aMesh = GetMeshDS();
4215
4216   const int           nbNodes = elem->NbNodes();
4217   const int         nbCorners = elem->NbCornerNodes();
4218   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4219                                                           polyhedron creation !!! */
4220   // Loop on elem nodes:
4221   // find new nodes and detect same nodes indices
4222   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4223   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4224   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4225   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4226
4227   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4228   vector<int> sames(nbNodes);
4229   vector<bool> isSingleNode(nbNodes);
4230
4231   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4232     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4233     const SMDS_MeshNode*                         node = nnIt->first;
4234     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4235     if ( listNewNodes.empty() )
4236       return;
4237
4238     itNN   [ iNode ] = listNewNodes.begin();
4239     prevNod[ iNode ] = node;
4240     nextNod[ iNode ] = listNewNodes.front();
4241
4242     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4243                                                              corner node of linear */
4244     if ( prevNod[ iNode ] != nextNod [ iNode ])
4245       nbDouble += !isSingleNode[iNode];
4246
4247     if( iNode < nbCorners ) { // check corners only
4248       if ( prevNod[ iNode ] == nextNod [ iNode ])
4249         sames[nbSame++] = iNode;
4250       else
4251         iNotSameNode = iNode;
4252     }
4253   }
4254
4255   if ( nbSame == nbNodes || nbSame > 2) {
4256     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4257     return;
4258   }
4259
4260   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4261   {
4262     // fix nodes order to have bottom normal external
4263     if ( baseType == SMDSEntity_Polygon )
4264     {
4265       std::reverse( itNN.begin(), itNN.end() );
4266       std::reverse( prevNod.begin(), prevNod.end() );
4267       std::reverse( midlNod.begin(), midlNod.end() );
4268       std::reverse( nextNod.begin(), nextNod.end() );
4269       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4270     }
4271     else
4272     {
4273       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4274       SMDS_MeshCell::applyInterlace( ind, itNN );
4275       SMDS_MeshCell::applyInterlace( ind, prevNod );
4276       SMDS_MeshCell::applyInterlace( ind, nextNod );
4277       SMDS_MeshCell::applyInterlace( ind, midlNod );
4278       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4279       if ( nbSame > 0 )
4280       {
4281         sames[nbSame] = iNotSameNode;
4282         for ( int j = 0; j <= nbSame; ++j )
4283           for ( size_t i = 0; i < ind.size(); ++i )
4284             if ( ind[i] == sames[j] )
4285             {
4286               sames[j] = i;
4287               break;
4288             }
4289         iNotSameNode = sames[nbSame];
4290       }
4291     }
4292   }
4293   else if ( elem->GetType() == SMDSAbs_Edge )
4294   {
4295     // orient a new face same as adjacent one
4296     int i1, i2;
4297     const SMDS_MeshElement* e;
4298     TIDSortedElemSet dummy;
4299     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4300         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4301         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4302     {
4303       // there is an adjacent face, check order of nodes in it
4304       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4305       if ( sameOrder )
4306       {
4307         std::swap( itNN[0],    itNN[1] );
4308         std::swap( prevNod[0], prevNod[1] );
4309         std::swap( nextNod[0], nextNod[1] );
4310         std::swap( isSingleNode[0], isSingleNode[1] );
4311         if ( nbSame > 0 )
4312           sames[0] = 1 - sames[0];
4313         iNotSameNode = 1 - iNotSameNode;
4314       }
4315     }
4316   }
4317
4318   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4319   if ( nbSame > 0 ) {
4320     iSameNode    = sames[ nbSame-1 ];
4321     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4322     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4323     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4324   }
4325
4326   if ( baseType == SMDSEntity_Polygon )
4327   {
4328     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4329     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4330   }
4331   else if ( baseType == SMDSEntity_Quad_Polygon )
4332   {
4333     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4334     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4335   }
4336
4337   // make new elements
4338   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4339   {
4340     // get next nodes
4341     for ( iNode = 0; iNode < nbNodes; iNode++ )
4342     {
4343       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4344       nextNod[ iNode ] = *itNN[ iNode ]++;
4345     }
4346
4347     SMDS_MeshElement* aNewElem = 0;
4348     /*if(!elem->IsPoly())*/ {
4349       switch ( baseType ) {
4350       case SMDSEntity_0D:
4351       case SMDSEntity_Node: { // sweep NODE
4352         if ( nbSame == 0 ) {
4353           if ( isSingleNode[0] )
4354             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4355           else
4356             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4357         }
4358         else
4359           return;
4360         break;
4361       }
4362       case SMDSEntity_Edge: { // sweep EDGE
4363         if ( nbDouble == 0 )
4364         {
4365           if ( nbSame == 0 ) // ---> quadrangle
4366             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4367                                       nextNod[ 1 ], nextNod[ 0 ] );
4368           else               // ---> triangle
4369             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4370                                       nextNod[ iNotSameNode ] );
4371         }
4372         else                 // ---> polygon
4373         {
4374           vector<const SMDS_MeshNode*> poly_nodes;
4375           poly_nodes.push_back( prevNod[0] );
4376           poly_nodes.push_back( prevNod[1] );
4377           if ( prevNod[1] != nextNod[1] )
4378           {
4379             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4380             poly_nodes.push_back( nextNod[1] );
4381           }
4382           if ( prevNod[0] != nextNod[0] )
4383           {
4384             poly_nodes.push_back( nextNod[0] );
4385             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4386           }
4387           switch ( poly_nodes.size() ) {
4388           case 3:
4389             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4390             break;
4391           case 4:
4392             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4393                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4394             break;
4395           default:
4396             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4397           }
4398         }
4399         break;
4400       }
4401       case SMDSEntity_Triangle: // TRIANGLE --->
4402       {
4403         if ( nbDouble > 0 ) break;
4404         if ( nbSame == 0 )       // ---> pentahedron
4405           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4406                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4407
4408         else if ( nbSame == 1 )  // ---> pyramid
4409           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4410                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4411                                        nextNod[ iSameNode ]);
4412
4413         else // 2 same nodes:       ---> tetrahedron
4414           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4415                                        nextNod[ iNotSameNode ]);
4416         break;
4417       }
4418       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4419       {
4420         if ( nbSame == 2 )
4421           return;
4422         if ( nbDouble+nbSame == 2 )
4423         {
4424           if(nbSame==0) {      // ---> quadratic quadrangle
4425             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4426                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4427           }
4428           else { //(nbSame==1) // ---> quadratic triangle
4429             if(sames[0]==2) {
4430               return; // medium node on axis
4431             }
4432             else if(sames[0]==0)
4433               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4434                                         prevNod[2], midlNod[1], nextNod[2] );
4435             else // sames[0]==1
4436               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4437                                         prevNod[2], nextNod[2], midlNod[0]);
4438           }
4439         }
4440         else if ( nbDouble == 3 )
4441         {
4442           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4443             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4444                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4445           }
4446         }
4447         else
4448           return;
4449         break;
4450       }
4451       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4452         if ( nbDouble > 0 ) break;
4453
4454         if ( nbSame == 0 )       // ---> hexahedron
4455           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4456                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4457
4458         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4459           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4460                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4461                                        nextNod[ iSameNode ]);
4462           newElems.push_back( aNewElem );
4463           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4464                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4465                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4466         }
4467         else if ( nbSame == 2 ) { // ---> pentahedron
4468           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4469             // iBeforeSame is same too
4470             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4471                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4472                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4473           else
4474             // iAfterSame is same too
4475             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4476                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4477                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4478         }
4479         break;
4480       }
4481       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4482       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4483         if ( nbDouble+nbSame != 3 ) break;
4484         if(nbSame==0) {
4485           // --->  pentahedron with 15 nodes
4486           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4487                                        nextNod[0], nextNod[1], nextNod[2],
4488                                        prevNod[3], prevNod[4], prevNod[5],
4489                                        nextNod[3], nextNod[4], nextNod[5],
4490                                        midlNod[0], midlNod[1], midlNod[2]);
4491         }
4492         else if(nbSame==1) {
4493           // --->  2d order pyramid of 13 nodes
4494           int apex = iSameNode;
4495           int i0 = ( apex + 1 ) % nbCorners;
4496           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4497           int i0a = apex + 3;
4498           int i1a = i1 + 3;
4499           int i01 = i0 + 3;
4500           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4501                                       nextNod[i0], nextNod[i1], prevNod[apex],
4502                                       prevNod[i01], midlNod[i0],
4503                                       nextNod[i01], midlNod[i1],
4504                                       prevNod[i1a], prevNod[i0a],
4505                                       nextNod[i0a], nextNod[i1a]);
4506         }
4507         else if(nbSame==2) {
4508           // --->  2d order tetrahedron of 10 nodes
4509           int n1 = iNotSameNode;
4510           int n2 = ( n1 + 1             ) % nbCorners;
4511           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4512           int n12 = n1 + 3;
4513           int n23 = n2 + 3;
4514           int n31 = n3 + 3;
4515           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4516                                        prevNod[n12], prevNod[n23], prevNod[n31],
4517                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4522         if( nbSame == 0 ) {
4523           if ( nbDouble != 4 ) break;
4524           // --->  hexahedron with 20 nodes
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4526                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4527                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4528                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4529                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4530         }
4531         else if(nbSame==1) {
4532           // ---> pyramid + pentahedron - can not be created since it is needed
4533           // additional middle node at the center of face
4534           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4535           return;
4536         }
4537         else if( nbSame == 2 ) {
4538           if ( nbDouble != 2 ) break;
4539           // --->  2d order Pentahedron with 15 nodes
4540           int n1,n2,n4,n5;
4541           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4542             // iBeforeSame is same too
4543             n1 = iBeforeSame;
4544             n2 = iOpposSame;
4545             n4 = iSameNode;
4546             n5 = iAfterSame;
4547           }
4548           else {
4549             // iAfterSame is same too
4550             n1 = iSameNode;
4551             n2 = iBeforeSame;
4552             n4 = iAfterSame;
4553             n5 = iOpposSame;
4554           }
4555           int n12 = n2 + 4;
4556           int n45 = n4 + 4;
4557           int n14 = n1 + 4;
4558           int n25 = n5 + 4;
4559           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4560                                        prevNod[n4], prevNod[n5], nextNod[n5],
4561                                        prevNod[n12], midlNod[n2], nextNod[n12],
4562                                        prevNod[n45], midlNod[n5], nextNod[n45],
4563                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4564         }
4565         break;
4566       }
4567       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4568
4569         if( nbSame == 0 && nbDouble == 9 ) {
4570           // --->  tri-quadratic hexahedron with 27 nodes
4571           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4572                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4573                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4574                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4575                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4576                                        prevNod[8], // bottom center
4577                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4578                                        nextNod[8], // top center
4579                                        midlNod[8]);// elem center
4580         }
4581         else
4582         {
4583           return;
4584         }
4585         break;
4586       }
4587       case SMDSEntity_Polygon: { // sweep POLYGON
4588
4589         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4590           // --->  hexagonal prism
4591           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4592                                        prevNod[3], prevNod[4], prevNod[5],
4593                                        nextNod[0], nextNod[1], nextNod[2],
4594                                        nextNod[3], nextNod[4], nextNod[5]);
4595         }
4596         break;
4597       }
4598       case SMDSEntity_Ball:
4599         return;
4600
4601       default:
4602         break;
4603       } // switch ( baseType )
4604     } // scope
4605
4606     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4607     {
4608       if ( baseType != SMDSEntity_Polygon )
4609       {
4610         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4611         SMDS_MeshCell::applyInterlace( ind, prevNod );
4612         SMDS_MeshCell::applyInterlace( ind, nextNod );
4613         SMDS_MeshCell::applyInterlace( ind, midlNod );
4614         SMDS_MeshCell::applyInterlace( ind, itNN );
4615         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4616         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4617       }
4618       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4619       vector<int> quantities (nbNodes + 2);
4620       polyedre_nodes.clear();
4621       quantities.clear();
4622
4623       // bottom of prism
4624       for (int inode = 0; inode < nbNodes; inode++)
4625         polyedre_nodes.push_back( prevNod[inode] );
4626       quantities.push_back( nbNodes );
4627
4628       // top of prism
4629       polyedre_nodes.push_back( nextNod[0] );
4630       for (int inode = nbNodes; inode-1; --inode )
4631         polyedre_nodes.push_back( nextNod[inode-1] );
4632       quantities.push_back( nbNodes );
4633
4634       // side faces
4635       // 3--6--2
4636       // |     |
4637       // 7     5
4638       // |     |
4639       // 0--4--1
4640       const int iQuad = elem->IsQuadratic();
4641       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4642       {
4643         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4644         int inextface = (iface+1+iQuad) % nbNodes;
4645         int imid      = (iface+1) % nbNodes;
4646         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4647         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4648         polyedre_nodes.push_back( prevNod[iface] );             // 1
4649         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4650         {
4651           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4652           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4653         }
4654         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4655         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4656         {
4657           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4658           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4659         }
4660         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4661         if ( nbFaceNodes > 2 )
4662           quantities.push_back( nbFaceNodes );
4663         else // degenerated face
4664           polyedre_nodes.resize( prevNbNodes );
4665       }
4666       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4667
4668     } // try to create a polyherdal prism
4669
4670     if ( aNewElem ) {
4671       newElems.push_back( aNewElem );
4672       myLastCreatedElems.push_back(aNewElem);
4673       srcElements.push_back( elem );
4674     }
4675
4676     // set new prev nodes
4677     for ( iNode = 0; iNode < nbNodes; iNode++ )
4678       prevNod[ iNode ] = nextNod[ iNode ];
4679
4680   } // loop on steps
4681 }
4682
4683 //=======================================================================
4684 /*!
4685  * \brief Create 1D and 2D elements around swept elements
4686  * \param mapNewNodes - source nodes and ones generated from them
4687  * \param newElemsMap - source elements and ones generated from them
4688  * \param elemNewNodesMap - nodes generated from each node of each element
4689  * \param elemSet - all swept elements
4690  * \param nbSteps - number of sweeping steps
4691  * \param srcElements - to append elem for each generated element
4692  */
4693 //=======================================================================
4694
4695 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4696                                   TTElemOfElemListMap &    newElemsMap,
4697                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4698                                   TIDSortedElemSet&        elemSet,
4699                                   const int                nbSteps,
4700                                   SMESH_SequenceOfElemPtr& srcElements)
4701 {
4702   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4703   SMESHDS_Mesh* aMesh = GetMeshDS();
4704
4705   // Find nodes belonging to only one initial element - sweep them into edges.
4706
4707   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4708   for ( ; nList != mapNewNodes.end(); nList++ )
4709   {
4710     const SMDS_MeshNode* node =
4711       static_cast<const SMDS_MeshNode*>( nList->first );
4712     if ( newElemsMap.count( node ))
4713       continue; // node was extruded into edge
4714     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4715     int nbInitElems = 0;
4716     const SMDS_MeshElement* el = 0;
4717     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4718     while ( eIt->more() && nbInitElems < 2 ) {
4719       const SMDS_MeshElement* e = eIt->next();
4720       SMDSAbs_ElementType  type = e->GetType();
4721       if ( type == SMDSAbs_Volume ||
4722            type < highType ||
4723            !elemSet.count(e))
4724         continue;
4725       if ( type > highType ) {
4726         nbInitElems = 0;
4727         highType    = type;
4728       }
4729       el = e;
4730       ++nbInitElems;
4731     }
4732     if ( nbInitElems == 1 ) {
4733       bool NotCreateEdge = el && el->IsMediumNode(node);
4734       if(!NotCreateEdge) {
4735         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4736         list<const SMDS_MeshElement*> newEdges;
4737         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4738       }
4739     }
4740   }
4741
4742   // Make a ceiling for each element ie an equal element of last new nodes.
4743   // Find free links of faces - make edges and sweep them into faces.
4744
4745   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4746
4747   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4748   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4749   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4750   {
4751     const SMDS_MeshElement* elem = itElem->first;
4752     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4753
4754     if(itElem->second.size()==0) continue;
4755
4756     const bool isQuadratic = elem->IsQuadratic();
4757
4758     if ( elem->GetType() == SMDSAbs_Edge ) {
4759       // create a ceiling edge
4760       if ( !isQuadratic ) {
4761         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4762                                vecNewNodes[ 1 ]->second.back())) {
4763           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4764                                                       vecNewNodes[ 1 ]->second.back()));
4765           srcElements.push_back( elem );
4766         }
4767       }
4768       else {
4769         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4770                                vecNewNodes[ 1 ]->second.back(),
4771                                vecNewNodes[ 2 ]->second.back())) {
4772           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4773                                                       vecNewNodes[ 1 ]->second.back(),
4774                                                       vecNewNodes[ 2 ]->second.back()));
4775           srcElements.push_back( elem );
4776         }
4777       }
4778     }
4779     if ( elem->GetType() != SMDSAbs_Face )
4780       continue;
4781
4782     bool hasFreeLinks = false;
4783
4784     TIDSortedElemSet avoidSet;
4785     avoidSet.insert( elem );
4786
4787     set<const SMDS_MeshNode*> aFaceLastNodes;
4788     int iNode, nbNodes = vecNewNodes.size();
4789     if ( !isQuadratic ) {
4790       // loop on the face nodes
4791       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4792         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4793         // look for free links of the face
4794         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4795         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4796         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4797         // check if a link n1-n2 is free
4798         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4799           hasFreeLinks = true;
4800           // make a new edge and a ceiling for a new edge
4801           const SMDS_MeshElement* edge;
4802           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4803             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4804             srcElements.push_back( myLastCreatedElems.back() );
4805           }
4806           n1 = vecNewNodes[ iNode ]->second.back();
4807           n2 = vecNewNodes[ iNext ]->second.back();
4808           if ( !aMesh->FindEdge( n1, n2 )) {
4809             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4810             srcElements.push_back( edge );
4811           }
4812         }
4813       }
4814     }
4815     else { // elem is quadratic face
4816       int nbn = nbNodes/2;
4817       for ( iNode = 0; iNode < nbn; iNode++ ) {
4818         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4819         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4820         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4821         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4822         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4823         // check if a link is free
4824         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4825              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4826              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4827           hasFreeLinks = true;
4828           // make an edge and a ceiling for a new edge
4829           // find medium node
4830           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4831             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4832             srcElements.push_back( elem );
4833           }
4834           n1 = vecNewNodes[ iNode ]->second.back();
4835           n2 = vecNewNodes[ iNext ]->second.back();
4836           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4837           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4838             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4839             srcElements.push_back( elem );
4840           }
4841         }
4842       }
4843       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4844         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4845       }
4846     }
4847
4848     // sweep free links into faces
4849
4850     if ( hasFreeLinks ) {
4851       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4852       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4853
4854       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4855       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4856       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4857         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4858         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4859       }
4860       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4861         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4862         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4863       }
4864       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4865         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4866         std::advance( v, volNb );
4867         // find indices of free faces of a volume and their source edges
4868         list< int > freeInd;
4869         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4870         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4871         int iF, nbF = vTool.NbFaces();
4872         for ( iF = 0; iF < nbF; iF ++ ) {
4873           if ( vTool.IsFreeFace( iF ) &&
4874                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4875                initNodeSet != faceNodeSet) // except an initial face
4876           {
4877             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4878               continue;
4879             if ( faceNodeSet == initNodeSetNoCenter )
4880               continue;
4881             freeInd.push_back( iF );
4882             // find source edge of a free face iF
4883             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4884             vector<const SMDS_MeshNode*>::iterator lastCommom;
4885             commonNodes.resize( nbNodes, 0 );
4886             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4887                                                 initNodeSet.begin(), initNodeSet.end(),
4888                                                 commonNodes.begin());
4889             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4890               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4891             else
4892               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4893 #ifdef _DEBUG_
4894             if ( !srcEdges.back() )
4895             {
4896               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4897                    << iF << " of volume #" << vTool.ID() << endl;
4898             }
4899 #endif
4900           }
4901         }
4902         if ( freeInd.empty() )
4903           continue;
4904
4905         // create wall faces for all steps;
4906         // if such a face has been already created by sweep of edge,
4907         // assure that its orientation is OK
4908         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4909         {
4910           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4911           vTool.SetExternalNormal();
4912           const int nextShift = vTool.IsForward() ? +1 : -1;
4913           list< int >::iterator ind = freeInd.begin();
4914           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4915           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4916           {
4917             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4918             int nbn = vTool.NbFaceNodes( *ind );
4919             const SMDS_MeshElement * f = 0;
4920             if ( nbn == 3 )              ///// triangle
4921             {
4922               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4923               if ( !f ||
4924                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4925               {
4926                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4927                                                      nodes[ 1 ],
4928                                                      nodes[ 1 + nextShift ] };
4929                 if ( f )
4930                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4931                 else
4932                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4933                                                                newOrder[ 2 ] ));
4934               }
4935             }
4936             else if ( nbn == 4 )       ///// quadrangle
4937             {
4938               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4939               if ( !f ||
4940                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4943                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4944                 if ( f )
4945                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4946                 else
4947                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4948                                                                newOrder[ 2 ], newOrder[ 3 ]));
4949               }
4950             }
4951             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4952             {
4953               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4954               if ( !f ||
4955                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4956               {
4957                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4958                                                      nodes[2],
4959                                                      nodes[2 + 2*nextShift],
4960                                                      nodes[3 - 2*nextShift],
4961                                                      nodes[3],
4962                                                      nodes[3 + 2*nextShift]};
4963                 if ( f )
4964                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4965                 else
4966                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4967                                                                newOrder[ 1 ],
4968                                                                newOrder[ 2 ],
4969                                                                newOrder[ 3 ],
4970                                                                newOrder[ 4 ],
4971                                                                newOrder[ 5 ] ));
4972               }
4973             }
4974             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4975             {
4976               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4977                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4978               if ( !f ||
4979                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4980               {
4981                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4982                                                      nodes[4 - 2*nextShift],
4983                                                      nodes[4],
4984                                                      nodes[4 + 2*nextShift],
4985                                                      nodes[1],
4986                                                      nodes[5 - 2*nextShift],
4987                                                      nodes[5],
4988                                                      nodes[5 + 2*nextShift] };
4989                 if ( f )
4990                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4991                 else
4992                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4993                                                               newOrder[ 2 ], newOrder[ 3 ],
4994                                                               newOrder[ 4 ], newOrder[ 5 ],
4995                                                               newOrder[ 6 ], newOrder[ 7 ]));
4996               }
4997             }
4998             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4999             {
5000               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5001                                       SMDSAbs_Face, /*noMedium=*/false);
5002               if ( !f ||
5003                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5004               {
5005                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5006                                                      nodes[4 - 2*nextShift],
5007                                                      nodes[4],
5008                                                      nodes[4 + 2*nextShift],
5009                                                      nodes[1],
5010                                                      nodes[5 - 2*nextShift],
5011                                                      nodes[5],
5012                                                      nodes[5 + 2*nextShift],
5013                                                      nodes[8] };
5014                 if ( f )
5015                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5016                 else
5017                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5018                                                               newOrder[ 2 ], newOrder[ 3 ],
5019                                                               newOrder[ 4 ], newOrder[ 5 ],
5020                                                               newOrder[ 6 ], newOrder[ 7 ],
5021                                                               newOrder[ 8 ]));
5022               }
5023             }
5024             else  //////// polygon
5025             {
5026               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5027               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5028               if ( !f ||
5029                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5030               {
5031                 if ( !vTool.IsForward() )
5032                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5033                 if ( f )
5034                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5035                 else
5036                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5037               }
5038             }
5039
5040             while ( srcElements.size() < myLastCreatedElems.size() )
5041               srcElements.push_back( *srcEdge );
5042
5043           }  // loop on free faces
5044
5045           // go to the next volume
5046           iVol = 0;
5047           while ( iVol++ < nbVolumesByStep ) v++;
5048
5049         } // loop on steps
5050       } // loop on volumes of one step
5051     } // sweep free links into faces
5052
5053     // Make a ceiling face with a normal external to a volume
5054
5055     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5056     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5057     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5058
5059     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5060       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5061       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5062     }
5063     if ( iF >= 0 )
5064     {
5065       lastVol.SetExternalNormal();
5066       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5067       const               int nbn = lastVol.NbFaceNodes( iF );
5068       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5069       if ( !hasFreeLinks ||
5070            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5071       {
5072         const vector<int>& interlace =
5073           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5074         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5075
5076         AddElement( nodeVec, anyFace.Init( elem ));
5077
5078         while ( srcElements.size() < myLastCreatedElems.size() )
5079           srcElements.push_back( elem );
5080       }
5081     }
5082   } // loop on swept elements
5083 }
5084
5085 //=======================================================================
5086 //function : RotationSweep
5087 //purpose  :
5088 //=======================================================================
5089
5090 SMESH_MeshEditor::PGroupIDs
5091 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5092                                 const gp_Ax1&      theAxis,
5093                                 const double       theAngle,
5094                                 const int          theNbSteps,
5095                                 const double       theTol,
5096                                 const bool         theMakeGroups,
5097                                 const bool         theMakeWalls)
5098 {
5099   ClearLastCreated();
5100
5101   setElemsFirst( theElemSets );
5102   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5103   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5104
5105   // source elements for each generated one
5106   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5107   srcElems.reserve( theElemSets[0].size() );
5108   srcNodes.reserve( theElemSets[1].size() );
5109
5110   gp_Trsf aTrsf;
5111   aTrsf.SetRotation( theAxis, theAngle );
5112   gp_Trsf aTrsf2;
5113   aTrsf2.SetRotation( theAxis, theAngle/2. );
5114
5115   gp_Lin aLine( theAxis );
5116   double aSqTol = theTol * theTol;
5117
5118   SMESHDS_Mesh* aMesh = GetMeshDS();
5119
5120   TNodeOfNodeListMap mapNewNodes;
5121   TElemOfVecOfNnlmiMap mapElemNewNodes;
5122   TTElemOfElemListMap newElemsMap;
5123
5124   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5125                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5126                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5127   // loop on theElemSets
5128   TIDSortedElemSet::iterator itElem;
5129   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5130   {
5131     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5132     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5133       const SMDS_MeshElement* elem = *itElem;
5134       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5135         continue;
5136       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5137       newNodesItVec.reserve( elem->NbNodes() );
5138
5139       // loop on elem nodes
5140       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5141       while ( itN->more() )
5142       {
5143         const SMDS_MeshNode* node = cast2Node( itN->next() );
5144
5145         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5146         double coord[3];
5147         aXYZ.Coord( coord[0], coord[1], coord[2] );
5148         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5149
5150         // check if a node has been already sweeped
5151         TNodeOfNodeListMapItr nIt =
5152           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5153         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5154         if ( listNewNodes.empty() )
5155         {
5156           // check if we are to create medium nodes between corner ones
5157           bool needMediumNodes = false;
5158           if ( isQuadraticMesh )
5159           {
5160             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5161             while (it->more() && !needMediumNodes )
5162             {
5163               const SMDS_MeshElement* invElem = it->next();
5164               if ( invElem != elem && !theElems.count( invElem )) continue;
5165               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5166               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5167                 needMediumNodes = true;
5168             }
5169           }
5170
5171           // make new nodes
5172           const SMDS_MeshNode * newNode = node;
5173           for ( int i = 0; i < theNbSteps; i++ ) {
5174             if ( !isOnAxis ) {
5175               if ( needMediumNodes )  // create a medium node
5176               {
5177                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5178                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5179                 myLastCreatedNodes.push_back(newNode);
5180                 srcNodes.push_back( node );
5181                 listNewNodes.push_back( newNode );
5182                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5183               }
5184               else {
5185                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5186               }
5187               // create a corner node
5188               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5189               myLastCreatedNodes.push_back(newNode);
5190               srcNodes.push_back( node );
5191               listNewNodes.push_back( newNode );
5192             }
5193             else {
5194               listNewNodes.push_back( newNode );
5195               // if ( needMediumNodes )
5196               //   listNewNodes.push_back( newNode );
5197             }
5198           }
5199         }
5200         newNodesItVec.push_back( nIt );
5201       }
5202       // make new elements
5203       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5204     }
5205   }
5206
5207   if ( theMakeWalls )
5208     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5209
5210   PGroupIDs newGroupIDs;
5211   if ( theMakeGroups )
5212     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5213
5214   return newGroupIDs;
5215 }
5216
5217 //=======================================================================
5218 //function : ExtrusParam
5219 //purpose  : standard construction
5220 //=======================================================================
5221
5222 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5223                                             const int                theNbSteps,
5224                                             const std::list<double>& theScales,
5225                                             const std::list<double>& theAngles,
5226                                             const gp_XYZ*            theBasePoint,
5227                                             const int                theFlags,
5228                                             const double             theTolerance):
5229   myDir( theStep ),
5230   myBaseP( Precision::Infinite(), 0, 0 ),
5231   myFlags( theFlags ),
5232   myTolerance( theTolerance ),
5233   myElemsToUse( NULL )
5234 {
5235   mySteps = new TColStd_HSequenceOfReal;
5236   const double stepSize = theStep.Magnitude();
5237   for (int i=1; i<=theNbSteps; i++ )
5238     mySteps->Append( stepSize );
5239
5240   if ( !theScales.empty() )
5241   {
5242     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5243       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5244
5245     // add medium scales
5246     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5247     myScales.reserve( theNbSteps * 2 );
5248     myScales.push_back( 0.5 * ( *s1 + 1. ));
5249     myScales.push_back( *s1 );
5250     for ( ; s2 != theScales.end(); s1 = s2++ )
5251     {
5252       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5253       myScales.push_back( *s2 );
5254     }
5255   }
5256
5257   if ( !theAngles.empty() )
5258   {
5259     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5260     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5261       linearAngleVariation( theNbSteps, angles );
5262
5263     // accumulate angles
5264     double angle = 0;
5265     int nbAngles = 0;
5266     std::list<double>::iterator a1 = angles.begin(), a2;
5267     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5268     {
5269       angle += *a1;
5270       *a1 = angle;
5271     }
5272     while ( nbAngles++ < theNbSteps )
5273       angles.push_back( angles.back() );
5274
5275     // add medium angles
5276     a2 = angles.begin(), a1 = a2++;
5277     myAngles.push_back( 0.5 * *a1 );
5278     myAngles.push_back( *a1 );
5279     for ( ; a2 != angles.end(); a1 = a2++ )
5280     {
5281       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5282       myAngles.push_back( *a2 );
5283     }
5284   }
5285
5286   if ( theBasePoint )
5287   {
5288     myBaseP = *theBasePoint;
5289   }
5290
5291   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5292       ( theTolerance > 0 ))
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5295   }
5296   else
5297   {
5298     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5299   }
5300 }
5301
5302 //=======================================================================
5303 //function : ExtrusParam
5304 //purpose  : steps are given explicitly
5305 //=======================================================================
5306
5307 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5308                                             Handle(TColStd_HSequenceOfReal) theSteps,
5309                                             const int                       theFlags,
5310                                             const double                    theTolerance):
5311   myDir( theDir ),
5312   mySteps( theSteps ),
5313   myFlags( theFlags ),
5314   myTolerance( theTolerance ),
5315   myElemsToUse( NULL )
5316 {
5317   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5318       ( theTolerance > 0 ))
5319   {
5320     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5321   }
5322   else
5323   {
5324     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5325   }
5326 }
5327
5328 //=======================================================================
5329 //function : ExtrusParam
5330 //purpose  : for extrusion by normal
5331 //=======================================================================
5332
5333 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5334                                             const int    theNbSteps,
5335                                             const int    theFlags,
5336                                             const int    theDim ):
5337   myDir( 1,0,0 ),
5338   mySteps( new TColStd_HSequenceOfReal ),
5339   myFlags( theFlags ),
5340   myTolerance( 0 ),
5341   myElemsToUse( NULL )
5342 {
5343   for (int i = 0; i < theNbSteps; i++ )
5344     mySteps->Append( theStepSize );
5345
5346   if ( theDim == 1 )
5347   {
5348     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5349   }
5350   else
5351   {
5352     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5353   }
5354 }
5355
5356 //=======================================================================
5357 //function : ExtrusParam
5358 //purpose  : for extrusion along path
5359 //=======================================================================
5360
5361 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5362                                             const gp_Pnt*                   theBasePoint,
5363                                             const std::list<double>&        theScales,
5364                                             const bool                      theMakeGroups )
5365   : myBaseP( Precision::Infinite(), 0, 0 ),
5366     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5367     myPathPoints( thePoints )
5368 {
5369   if ( theBasePoint )
5370   {
5371     myBaseP = theBasePoint->XYZ();
5372   }
5373
5374   if ( !theScales.empty() )
5375   {
5376     // add medium scales
5377     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5378     myScales.reserve( thePoints.size() * 2 );
5379     myScales.push_back( 0.5 * ( 1. + *s1 ));
5380     myScales.push_back( *s1 );
5381     for ( ; s2 != theScales.end(); s1 = s2++ )
5382     {
5383       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5384       myScales.push_back( *s2 );
5385     }
5386   }
5387
5388   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5389 }
5390
5391 //=======================================================================
5392 //function : ExtrusParam::SetElementsToUse
5393 //purpose  : stores elements to use for extrusion by normal, depending on
5394 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5395 //           define myBaseP for scaling
5396 //=======================================================================
5397
5398 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5399                                                       const TIDSortedElemSet& nodes )
5400 {
5401   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5402
5403   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5404   {
5405     myBaseP.SetCoord( 0.,0.,0. );
5406     TIDSortedElemSet newNodes;
5407
5408     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5409     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5410     {
5411       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5412       TIDSortedElemSet::const_iterator itElem = elements.begin();
5413       for ( ; itElem != elements.end(); itElem++ )
5414       {
5415         const SMDS_MeshElement* elem = *itElem;
5416         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5417         while ( itN->more() ) {
5418           const SMDS_MeshElement* node = itN->next();
5419           if ( newNodes.insert( node ).second )
5420             myBaseP += SMESH_NodeXYZ( node );
5421         }
5422       }
5423     }
5424     myBaseP /= newNodes.size();
5425   }
5426 }
5427
5428 //=======================================================================
5429 //function : ExtrusParam::beginStepIter
5430 //purpose  : prepare iteration on steps
5431 //=======================================================================
5432
5433 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5434 {
5435   myWithMediumNodes = withMediumNodes;
5436   myNextStep = 1;
5437   myCurSteps.clear();
5438 }
5439 //=======================================================================
5440 //function : ExtrusParam::moreSteps
5441 //purpose  : are there more steps?
5442 //=======================================================================
5443
5444 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5445 {
5446   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5447 }
5448 //=======================================================================
5449 //function : ExtrusParam::nextStep
5450 //purpose  : returns the next step
5451 //=======================================================================
5452
5453 double SMESH_MeshEditor::ExtrusParam::nextStep()
5454 {
5455   double res = 0;
5456   if ( !myCurSteps.empty() )
5457   {
5458     res = myCurSteps.back();
5459     myCurSteps.pop_back();
5460   }
5461   else if ( myNextStep <= mySteps->Length() )
5462   {
5463     myCurSteps.push_back( mySteps->Value( myNextStep ));
5464     ++myNextStep;
5465     if ( myWithMediumNodes )
5466     {
5467       myCurSteps.back() /= 2.;
5468       myCurSteps.push_back( myCurSteps.back() );
5469     }
5470     res = nextStep();
5471   }
5472   return res;
5473 }
5474
5475 //=======================================================================
5476 //function : ExtrusParam::makeNodesByDir
5477 //purpose  : create nodes for standard extrusion
5478 //=======================================================================
5479
5480 int SMESH_MeshEditor::ExtrusParam::
5481 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5482                 const SMDS_MeshNode*              srcNode,
5483                 std::list<const SMDS_MeshNode*> & newNodes,
5484                 const bool                        makeMediumNodes)
5485 {
5486   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5487
5488   int nbNodes = 0;
5489   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5490   {
5491     p += myDir.XYZ() * nextStep();
5492     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5493     newNodes.push_back( newNode );
5494   }
5495
5496   if ( !myScales.empty() || !myAngles.empty() )
5497   {
5498     gp_XYZ  center = myBaseP;
5499     gp_Ax1  ratationAxis( center, myDir );
5500     gp_Trsf rotation;
5501
5502     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5503     size_t i = !makeMediumNodes;
5504     for ( beginStepIter( makeMediumNodes );
5505           moreSteps();
5506           ++nIt, i += 1 + !makeMediumNodes )
5507     {
5508       center += myDir.XYZ() * nextStep();
5509
5510       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5511       bool moved = false;
5512       if ( i < myScales.size() )
5513       {
5514         xyz = ( myScales[i] * ( xyz - center )) + center;
5515         moved = true;
5516       }
5517       if ( !myAngles.empty() )
5518       {
5519         rotation.SetRotation( ratationAxis, myAngles[i] );
5520         rotation.Transforms( xyz );
5521         moved = true;
5522       }
5523       if ( moved )
5524         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5525       else
5526         break;
5527     }
5528   }
5529   return nbNodes;
5530 }
5531
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByDirAndSew
5534 //purpose  : create nodes for standard extrusion with sewing
5535 //=======================================================================
5536
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5539                       const SMDS_MeshNode*              srcNode,
5540                       std::list<const SMDS_MeshNode*> & newNodes,
5541                       const bool                        makeMediumNodes)
5542 {
5543   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5544
5545   int nbNodes = 0;
5546   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5547   {
5548     P1 += myDir.XYZ() * nextStep();
5549
5550     // try to search in sequence of existing nodes
5551     // if myNodes.size()>0 we 'nave to use given sequence
5552     // else - use all nodes of mesh
5553     const SMDS_MeshNode * node = 0;
5554     if ( myNodes.Length() > 0 )
5555     {
5556       for ( int i = 1; i <= myNodes.Length(); i++ )
5557       {
5558         SMESH_NodeXYZ P2 = myNodes.Value(i);
5559         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5560         {
5561           node = myNodes.Value(i);
5562           break;
5563         }
5564       }
5565     }
5566     else
5567     {
5568       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5569       while(itn->more())
5570       {
5571         SMESH_NodeXYZ P2 = itn->next();
5572         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5573         {
5574           node = P2._node;
5575           break;
5576         }
5577       }
5578     }
5579
5580     if ( !node )
5581       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5582
5583     newNodes.push_back( node );
5584
5585   } // loop on steps
5586
5587   return nbNodes;
5588 }
5589
5590 //=======================================================================
5591 //function : ExtrusParam::makeNodesByNormal2D
5592 //purpose  : create nodes for extrusion using normals of faces
5593 //=======================================================================
5594
5595 int SMESH_MeshEditor::ExtrusParam::
5596 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5597                      const SMDS_MeshNode*              srcNode,
5598                      std::list<const SMDS_MeshNode*> & newNodes,
5599                      const bool                        makeMediumNodes)
5600 {
5601   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5602
5603   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5604
5605   // get normals to faces sharing srcNode
5606   vector< gp_XYZ > norms, baryCenters;
5607   gp_XYZ norm, avgNorm( 0,0,0 );
5608   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5609   while ( faceIt->more() )
5610   {
5611     const SMDS_MeshElement* face = faceIt->next();
5612     if ( myElemsToUse && !myElemsToUse->count( face ))
5613       continue;
5614     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5615     {
5616       norms.push_back( norm );
5617       avgNorm += norm;
5618       if ( !alongAvgNorm )
5619       {
5620         gp_XYZ bc(0,0,0);
5621         int nbN = 0;
5622         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5623           bc += SMESH_NodeXYZ( nIt->next() );
5624         baryCenters.push_back( bc / nbN );
5625       }
5626     }
5627   }
5628
5629   if ( norms.empty() ) return 0;
5630
5631   double normSize = avgNorm.Modulus();
5632   if ( normSize < std::numeric_limits<double>::min() )
5633     return 0;
5634
5635   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5636   {
5637     myDir = avgNorm;
5638     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5639   }
5640
5641   avgNorm /= normSize;
5642
5643   int nbNodes = 0;
5644   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5645   {
5646     gp_XYZ pNew = p;
5647     double stepSize = nextStep();
5648
5649     if ( norms.size() > 1 )
5650     {
5651       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5652       {
5653         // translate plane of a face
5654         baryCenters[ iF ] += norms[ iF ] * stepSize;
5655
5656         // find point of intersection of the face plane located at baryCenters[ iF ]
5657         // and avgNorm located at pNew
5658         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5659         double dot  = ( norms[ iF ] * avgNorm );
5660         if ( dot < std::numeric_limits<double>::min() )
5661           dot = stepSize * 1e-3;
5662         double step = -( norms[ iF ] * pNew + d ) / dot;
5663         pNew += step * avgNorm;
5664       }
5665     }
5666     else
5667     {
5668       pNew += stepSize * avgNorm;
5669     }
5670     p = pNew;
5671
5672     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5673     newNodes.push_back( newNode );
5674   }
5675   return nbNodes;
5676 }
5677
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesByNormal1D
5680 //purpose  : create nodes for extrusion using normals of edges
5681 //=======================================================================
5682
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5685                      const SMDS_MeshNode*              /*srcNode*/,
5686                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5687                      const bool                        /*makeMediumNodes*/)
5688 {
5689   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5690   return 0;
5691 }
5692
5693 //=======================================================================
5694 //function : ExtrusParam::makeNodesAlongTrack
5695 //purpose  : create nodes for extrusion along path
5696 //=======================================================================
5697
5698 int SMESH_MeshEditor::ExtrusParam::
5699 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5700                      const SMDS_MeshNode*              srcNode,
5701                      std::list<const SMDS_MeshNode*> & newNodes,
5702                      const bool                        makeMediumNodes)
5703 {
5704   const Standard_Real aTolAng=1.e-4;
5705
5706   gp_Pnt aV0x = myBaseP;
5707   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5708
5709   const PathPoint& aPP0 = myPathPoints[0];
5710   gp_Pnt aP0x = aPP0.myPnt;
5711   gp_Dir aDT0x= aPP0.myTgt;
5712
5713   std::vector< gp_Pnt > centers;
5714   centers.reserve( NbSteps() * 2 );
5715
5716   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5717
5718   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5719   {
5720     const PathPoint&  aPP  = myPathPoints[j];
5721     const gp_Pnt&     aP1x = aPP.myPnt;
5722     const gp_Dir&    aDT1x = aPP.myTgt;
5723
5724     // Translation
5725     gp_Vec aV01x( aP0x, aP1x );
5726     aTrsf.SetTranslation( aV01x );
5727     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5728     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5729
5730     // rotation 1 [ T1,T0 ]
5731     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5732     if ( fabs( aAngleT1T0 ) > aTolAng )
5733     {
5734       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5735       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5736
5737       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5738     }
5739
5740     // rotation 2
5741     if ( aPP.myAngle != 0. )
5742     {
5743       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5744       aPN1 = aPN1.Transformed( aTrsfRot );
5745     }
5746
5747     // make new node
5748     if ( makeMediumNodes )
5749     {
5750       // create additional node
5751       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5752       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5753       newNodes.push_back( newNode );
5754
5755     }
5756     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5757     newNodes.push_back( newNode );
5758
5759     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5760     centers.push_back( aV1x );
5761
5762     aPN0 = aPN1;
5763     aP0x = aP1x;
5764     aV0x = aV1x;
5765     aDT0x = aDT1x;
5766   }
5767
5768   // scale
5769   if ( !myScales.empty() )
5770   {
5771     gp_Trsf aTrsfScale;
5772     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5773     for ( size_t i = !makeMediumNodes;
5774           i < myScales.size() && node != newNodes.end();
5775           i += ( 1 + !makeMediumNodes ), ++node )
5776     {
5777       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5778       gp_Pnt aN = SMESH_NodeXYZ( *node );
5779       gp_Pnt aP = aN.Transformed( aTrsfScale );
5780       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5781     }
5782   }
5783
5784   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5785 }
5786
5787 //=======================================================================
5788 //function : ExtrusionSweep
5789 //purpose  :
5790 //=======================================================================
5791
5792 SMESH_MeshEditor::PGroupIDs
5793 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5794                                   const gp_Vec&        theStep,
5795                                   const int            theNbSteps,
5796                                   TTElemOfElemListMap& newElemsMap,
5797                                   const int            theFlags,
5798                                   const double         theTolerance)
5799 {
5800   std::list<double> dummy;
5801   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5802                        theFlags, theTolerance );
5803   return ExtrusionSweep( theElems, aParams, newElemsMap );
5804 }
5805
5806 namespace
5807 {
5808
5809 //=======================================================================
5810 //function : getOriFactor
5811 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5812 //           edge curve orientation
5813 //=======================================================================
5814
5815   double getOriFactor( const TopoDS_Edge&   edge,
5816                        const SMDS_MeshNode* n1,
5817                        const SMDS_MeshNode* n2,
5818                        SMESH_MesherHelper&  helper)
5819   {
5820     double u1 = helper.GetNodeU( edge, n1, n2 );
5821     double u2 = helper.GetNodeU( edge, n2, n1 );
5822     return u1 < u2 ? 1. : -1.;
5823   }
5824 }
5825
5826 //=======================================================================
5827 //function : ExtrusionSweep
5828 //purpose  :
5829 //=======================================================================
5830
5831 SMESH_MeshEditor::PGroupIDs
5832 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5833                                   ExtrusParam&         theParams,
5834                                   TTElemOfElemListMap& newElemsMap)
5835 {
5836   ClearLastCreated();
5837
5838   setElemsFirst( theElemSets );
5839   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5840   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5841
5842   // source elements for each generated one
5843   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5844   srcElems.reserve( theElemSets[0].size() );
5845   srcNodes.reserve( theElemSets[1].size() );
5846
5847   const int nbSteps = theParams.NbSteps();
5848   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5849
5850   TNodeOfNodeListMap   mapNewNodes;
5851   TElemOfVecOfNnlmiMap mapElemNewNodes;
5852
5853   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5854                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5855                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5856   // loop on theElems
5857   TIDSortedElemSet::iterator itElem;
5858   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5859   {
5860     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5861     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5862     {
5863       // check element type
5864       const SMDS_MeshElement* elem = *itElem;
5865       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5866         continue;
5867
5868       const size_t nbNodes = elem->NbNodes();
5869       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5870       newNodesItVec.reserve( nbNodes );
5871
5872       // loop on elem nodes
5873       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5874       while ( itN->more() )
5875       {
5876         // check if a node has been already sweeped
5877         const SMDS_MeshNode* node = itN->next();
5878         TNodeOfNodeListMap::iterator nIt =
5879           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5880         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5881         if ( listNewNodes.empty() )
5882         {
5883           // make new nodes
5884
5885           // check if we are to create medium nodes between corner ones
5886           bool needMediumNodes = false;
5887           if ( isQuadraticMesh )
5888           {
5889             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5890             while (it->more() && !needMediumNodes )
5891             {
5892               const SMDS_MeshElement* invElem = it->next();
5893               if ( invElem != elem && !theElems.count( invElem )) continue;
5894               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5895               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5896                 needMediumNodes = true;
5897             }
5898           }
5899           // create nodes for all steps
5900           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5901           {
5902             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5903             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5904             {
5905               myLastCreatedNodes.push_back( *newNodesIt );
5906               srcNodes.push_back( node );
5907             }
5908           }
5909           else
5910           {
5911             if ( theParams.ToMakeBoundary() )
5912             {
5913               GetMeshDS()->Modified();
5914               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5915             }
5916             break; // newNodesItVec will be shorter than nbNodes
5917           }
5918         }
5919         newNodesItVec.push_back( nIt );
5920       }
5921       // make new elements
5922       if ( newNodesItVec.size() == nbNodes )
5923         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5924     }
5925   }
5926
5927   if ( theParams.ToMakeBoundary() ) {
5928     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5929   }
5930   PGroupIDs newGroupIDs;
5931   if ( theParams.ToMakeGroups() )
5932     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5933
5934   return newGroupIDs;
5935 }
5936
5937 //=======================================================================
5938 //function : ExtrusionAlongTrack
5939 //purpose  :
5940 //=======================================================================
5941 SMESH_MeshEditor::Extrusion_Error
5942 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5943                                        SMESH_Mesh*          theTrackMesh,
5944                                        SMDS_ElemIteratorPtr theTrackIterator,
5945                                        const SMDS_MeshNode* theN1,
5946                                        std::list<double>&   theAngles,
5947                                        const bool           theAngleVariation,
5948                                        std::list<double>&   theScales,
5949                                        const bool           theScaleVariation,
5950                                        const gp_Pnt*        theRefPoint,
5951                                        const bool           theMakeGroups)
5952 {
5953   ClearLastCreated();
5954
5955   // 1. Check data
5956   if ( theElements[0].empty() && theElements[1].empty() )
5957     return EXTR_NO_ELEMENTS;
5958
5959   ASSERT( theTrackMesh );
5960   if ( ! theTrackIterator || !theTrackIterator->more() )
5961     return EXTR_NO_ELEMENTS;
5962
5963   // 2. Get ordered nodes
5964   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5965   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5966   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5967   if ( branchEdges.empty() )
5968     return EXTR_PATH_NOT_EDGE;
5969
5970   if ( branchEdges.size() > 1 )
5971     return EXTR_BAD_PATH_SHAPE;
5972
5973   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5974   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5975   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5976     return EXTR_BAD_STARTING_NODE;
5977
5978   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5979   {
5980     // add medium nodes to pathNodes
5981     std::vector< const SMDS_MeshNode* >    pathNodes2;
5982     std::vector< const SMDS_MeshElement* > pathEdges2;
5983     pathNodes2.reserve( pathNodes.size() * 2 );
5984     pathEdges2.reserve( pathEdges.size() * 2 );
5985     for ( size_t i = 0; i < pathEdges.size(); ++i )
5986     {
5987       pathNodes2.push_back( pathNodes[i] );
5988       pathEdges2.push_back( pathEdges[i] );
5989       if ( pathEdges[i]->IsQuadratic() )
5990       {
5991         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5992         pathEdges2.push_back( pathEdges[i] );
5993       }
5994     }
5995     pathNodes2.push_back( pathNodes.back() );
5996     pathEdges.swap( pathEdges2 );
5997     pathNodes.swap( pathNodes2 );
5998   }
5999
6000   // 3. Get path data at pathNodes
6001
6002   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6003
6004   if ( theAngleVariation )
6005     linearAngleVariation( points.size()-1, theAngles );
6006   if ( theScaleVariation )
6007     linearScaleVariation( points.size()-1, theScales );
6008
6009   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6010   std::list<double>::iterator angle = theAngles.begin();
6011
6012   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6013
6014   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6015   std::map< int, double >::iterator id2factor;
6016   SMESH_MesherHelper pathHelper( *theTrackMesh );
6017   gp_Pnt p; gp_Vec tangent;
6018   const double tol2 = gp::Resolution() * gp::Resolution();
6019
6020   for ( size_t i = 0; i < pathNodes.size(); ++i )
6021   {
6022     ExtrusParam::PathPoint & point = points[ i ];
6023
6024     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6025
6026     if ( angle != theAngles.end() )
6027       point.myAngle = *angle++;
6028
6029     tangent.SetCoord( 0,0,0 );
6030     const int          shapeID = pathNodes[ i ]->GetShapeID();
6031     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6032     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6033     switch ( shapeType )
6034     {
6035     case TopAbs_EDGE:
6036     {
6037       TopoDS_Edge edge = TopoDS::Edge( shape );
6038       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6039       if ( id2factor->second == 0 )
6040       {
6041         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6042         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6043       }
6044       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6045       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6046       curve->D1( u, p, tangent );
6047       tangent *= id2factor->second;
6048       break;
6049     }
6050     case TopAbs_VERTEX:
6051     {
6052       int nbEdges = 0;
6053       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6054       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6055       {
6056         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6057         for ( int di = -1; di <= 0; ++di )
6058         {
6059           size_t j = i + di;
6060           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6061           {
6062             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6063             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6064             if ( id2factor->second == 0 )
6065             {
6066               if ( j < i )
6067                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6068               else
6069                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6070             }
6071             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6072             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6073             gp_Vec du;
6074             curve->D1( u, p, du );
6075             double size2 = du.SquareMagnitude();
6076             if ( du.SquareMagnitude() > tol2 )
6077             {
6078               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6079               nbEdges++;
6080             }
6081             break;
6082           }
6083         }
6084       }
6085       if ( nbEdges > 0 )
6086         break;
6087     }
6088     // fall through
6089     default:
6090     {
6091       for ( int di = -1; di <= 1; di += 2 )
6092       {
6093         size_t j = i + di;
6094         if ( j < pathNodes.size() )
6095         {
6096           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6097           double size2 = dir.SquareMagnitude();
6098           if ( size2 > tol2 )
6099             tangent += dir.Divided( Sqrt( size2 )) * di;
6100         }
6101       }
6102     }
6103     } // switch ( shapeType )
6104
6105     if ( tangent.SquareMagnitude() < tol2 )
6106       return EXTR_CANT_GET_TANGENT;
6107
6108     point.myTgt = tangent;
6109
6110   } // loop on pathNodes
6111
6112
6113   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6114   TTElemOfElemListMap newElemsMap;
6115
6116   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6117
6118   return EXTR_OK;
6119 }
6120
6121 //=======================================================================
6122 //function : linearAngleVariation
6123 //purpose  : spread values over nbSteps
6124 //=======================================================================
6125
6126 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6127                                             list<double>& Angles)
6128 {
6129   int nbAngles = Angles.size();
6130   if( nbSteps > nbAngles && nbAngles > 0 )
6131   {
6132     vector<double> theAngles(nbAngles);
6133     theAngles.assign( Angles.begin(), Angles.end() );
6134
6135     list<double> res;
6136     double rAn2St = double( nbAngles ) / double( nbSteps );
6137     double angPrev = 0, angle;
6138     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6139     {
6140       double angCur = rAn2St * ( iSt+1 );
6141       double angCurFloor  = floor( angCur );
6142       double angPrevFloor = floor( angPrev );
6143       if ( angPrevFloor == angCurFloor )
6144         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6145       else {
6146         int iP = int( angPrevFloor );
6147         double angPrevCeil = ceil(angPrev);
6148         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6149
6150         int iC = int( angCurFloor );
6151         if ( iC < nbAngles )
6152           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6153
6154         iP = int( angPrevCeil );
6155         while ( iC-- > iP )
6156           angle += theAngles[ iC ];
6157       }
6158       res.push_back(angle);
6159       angPrev = angCur;
6160     }
6161     Angles.swap( res );
6162   }
6163 }
6164
6165 //=======================================================================
6166 //function : linearScaleVariation
6167 //purpose  : spread values over nbSteps 
6168 //=======================================================================
6169
6170 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6171                                             std::list<double>& theScales)
6172 {
6173   int nbScales = theScales.size();
6174   std::vector<double> myScales;
6175   myScales.reserve( theNbSteps );
6176   std::list<double>::const_iterator scale = theScales.begin();
6177   double prevScale = 1.0;
6178   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6179   {
6180     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6181     int    stDelta = Max( 1, iStep - myScales.size());
6182     double scDelta = ( *scale - prevScale ) / stDelta;
6183     for ( int iStep = 0; iStep < stDelta; ++iStep )
6184     {
6185       myScales.push_back( prevScale + scDelta );
6186       prevScale = myScales.back();
6187     }
6188     prevScale = *scale;
6189   }
6190   theScales.assign( myScales.begin(), myScales.end() );
6191 }
6192
6193 //================================================================================
6194 /*!
6195  * \brief Move or copy theElements applying theTrsf to their nodes
6196  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6197  *  \param theTrsf - transformation to apply
6198  *  \param theCopy - if true, create translated copies of theElems
6199  *  \param theMakeGroups - if true and theCopy, create translated groups
6200  *  \param theTargetMesh - mesh to copy translated elements into
6201  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6202  */
6203 //================================================================================
6204
6205 SMESH_MeshEditor::PGroupIDs
6206 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6207                              const gp_Trsf&     theTrsf,
6208                              const bool         theCopy,
6209                              const bool         theMakeGroups,
6210                              SMESH_Mesh*        theTargetMesh)
6211 {
6212   ClearLastCreated();
6213   myLastCreatedElems.reserve( theElems.size() );
6214
6215   bool needReverse = false;
6216   string groupPostfix;
6217   switch ( theTrsf.Form() ) {
6218   case gp_PntMirror:
6219     needReverse = true;
6220     groupPostfix = "mirrored";
6221     break;
6222   case gp_Ax1Mirror:
6223     groupPostfix = "mirrored";
6224     break;
6225   case gp_Ax2Mirror:
6226     needReverse = true;
6227     groupPostfix = "mirrored";
6228     break;
6229   case gp_Rotation:
6230     groupPostfix = "rotated";
6231     break;
6232   case gp_Translation:
6233     groupPostfix = "translated";
6234     break;
6235   case gp_Scale:
6236     groupPostfix = "scaled";
6237     break;
6238   case gp_CompoundTrsf: // different scale by axis
6239     groupPostfix = "scaled";
6240     break;
6241   default:
6242     needReverse = false;
6243     groupPostfix = "transformed";
6244   }
6245
6246   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6247   SMESHDS_Mesh* aMesh    = GetMeshDS();
6248
6249   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6250   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6251   SMESH_MeshEditor::ElemFeatures elemType;
6252
6253   // map old node to new one
6254   TNodeNodeMap nodeMap;
6255
6256   // elements sharing moved nodes; those of them which have all
6257   // nodes mirrored but are not in theElems are to be reversed
6258   TIDSortedElemSet inverseElemSet;
6259
6260   // source elements for each generated one
6261   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6262
6263   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6264   TIDSortedElemSet orphanNode;
6265
6266   if ( theElems.empty() ) // transform the whole mesh
6267   {
6268     // add all elements
6269     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6270     while ( eIt->more() ) theElems.insert( eIt->next() );
6271     // add orphan nodes
6272     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6273     while ( nIt->more() )
6274     {
6275       const SMDS_MeshNode* node = nIt->next();
6276       if ( node->NbInverseElements() == 0)
6277         orphanNode.insert( node );
6278     }
6279   }
6280
6281   // loop on elements to transform nodes : first orphan nodes then elems
6282   TIDSortedElemSet::iterator itElem;
6283   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6284   for (int i=0; i<2; i++)
6285     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6286     {
6287       const SMDS_MeshElement* elem = *itElem;
6288       if ( !elem )
6289         continue;
6290
6291       // loop on elem nodes
6292       double coord[3];
6293       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6294       while ( itN->more() )
6295       {
6296         const SMDS_MeshNode* node = cast2Node( itN->next() );
6297         // check if a node has been already transformed
6298         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6299           nodeMap.insert( make_pair ( node, node ));
6300         if ( !n2n_isnew.second )
6301           continue;
6302
6303         node->GetXYZ( coord );
6304         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6305         if ( theTargetMesh ) {
6306           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6307           n2n_isnew.first->second = newNode;
6308           myLastCreatedNodes.push_back(newNode);
6309           srcNodes.push_back( node );
6310         }
6311         else if ( theCopy ) {
6312           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6313           n2n_isnew.first->second = newNode;
6314           myLastCreatedNodes.push_back(newNode);
6315           srcNodes.push_back( node );
6316         }
6317         else {
6318           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6319           // node position on shape becomes invalid
6320           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6321             ( SMDS_SpacePosition::originSpacePosition() );
6322         }
6323
6324         // keep inverse elements
6325         if ( !theCopy && !theTargetMesh && needReverse ) {
6326           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6327           while ( invElemIt->more() ) {
6328             const SMDS_MeshElement* iel = invElemIt->next();
6329             inverseElemSet.insert( iel );
6330           }
6331         }
6332       }
6333     } // loop on elems in { &orphanNode, &theElems };
6334
6335   // either create new elements or reverse mirrored ones
6336   if ( !theCopy && !needReverse && !theTargetMesh )
6337     return PGroupIDs();
6338
6339   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6340
6341   // Replicate or reverse elements
6342
6343   std::vector<int> iForw;
6344   vector<const SMDS_MeshNode*> nodes;
6345   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6346   {
6347     const SMDS_MeshElement* elem = *itElem;
6348     if ( !elem ) continue;
6349
6350     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6351     size_t               nbNodes  = elem->NbNodes();
6352     if ( geomType == SMDSGeom_NONE ) continue; // node
6353
6354     nodes.resize( nbNodes );
6355
6356     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6357     {
6358       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6359       if ( !aPolyedre )
6360         continue;
6361       nodes.clear();
6362       bool allTransformed = true;
6363       int nbFaces = aPolyedre->NbFaces();
6364       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6365       {
6366         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6367         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6368         {
6369           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6370           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6371           if ( nodeMapIt == nodeMap.end() )
6372             allTransformed = false; // not all nodes transformed
6373           else
6374             nodes.push_back((*nodeMapIt).second);
6375         }
6376         if ( needReverse && allTransformed )
6377           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6378       }
6379       if ( !allTransformed )
6380         continue; // not all nodes transformed
6381     }
6382     else // ----------------------- the rest element types
6383     {
6384       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6385       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6386       const vector<int>&    i = needReverse ? iRev : iForw;
6387
6388       // find transformed nodes
6389       size_t iNode = 0;
6390       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6391       while ( itN->more() ) {
6392         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6393         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6394         if ( nodeMapIt == nodeMap.end() )
6395           break; // not all nodes transformed
6396         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6397       }
6398       if ( iNode != nbNodes )
6399         continue; // not all nodes transformed
6400     }
6401
6402     if ( editor ) {
6403       // copy in this or a new mesh
6404       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6405         srcElems.push_back( elem );
6406     }
6407     else {
6408       // reverse element as it was reversed by transformation
6409       if ( nbNodes > 2 )
6410         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6411     }
6412
6413   } // loop on elements
6414
6415   if ( editor && editor != this )
6416     myLastCreatedElems.swap( editor->myLastCreatedElems );
6417
6418   PGroupIDs newGroupIDs;
6419
6420   if ( ( theMakeGroups && theCopy ) ||
6421        ( theMakeGroups && theTargetMesh ) )
6422     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6423
6424   return newGroupIDs;
6425 }
6426
6427 //================================================================================
6428 /*!
6429  * \brief Make an offset mesh from a source 2D mesh
6430  *  \param [in] theElements - source faces
6431  *  \param [in] theValue - offset value
6432  *  \param [out] theTgtMesh - a mesh to add offset elements to
6433  *  \param [in] theMakeGroups - to generate groups
6434  *  \return PGroupIDs - IDs of created groups. NULL means failure
6435  */
6436 //================================================================================
6437
6438 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6439                                                       const double       theValue,
6440                                                       SMESH_Mesh*        theTgtMesh,
6441                                                       const bool         theMakeGroups,
6442                                                       const bool         theCopyElements,
6443                                                       const bool         theFixSelfIntersection)
6444 {
6445   SMESHDS_Mesh*    meshDS = GetMeshDS();
6446   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6447   SMESH_MeshEditor tgtEditor( theTgtMesh );
6448
6449   SMDS_ElemIteratorPtr eIt;
6450   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6452
6453   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6454   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6455   std::unique_ptr< SMDS_Mesh > offsetMesh
6456     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6457                                    theFixSelfIntersection,
6458                                    new2OldFaces, new2OldNodes ));
6459   if ( offsetMesh->NbElements() == 0 )
6460     return PGroupIDs(); // MakeOffset() failed
6461
6462
6463   if ( theTgtMesh == myMesh && !theCopyElements )
6464   {
6465     // clear the source elements
6466     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6467     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6468     while ( eIt->more() )
6469       meshDS->RemoveFreeElement( eIt->next(), 0 );
6470   }
6471
6472   // offsetMesh->Modified();
6473   // offsetMesh->CompactMesh(); // make IDs start from 1
6474
6475   // source elements for each generated one
6476   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6477   srcElems.reserve( new2OldFaces.size() );
6478   srcNodes.reserve( new2OldNodes.size() );
6479
6480   ClearLastCreated();
6481   myLastCreatedElems.reserve( new2OldFaces.size() );
6482   myLastCreatedNodes.reserve( new2OldNodes.size() );
6483
6484   // copy offsetMesh to theTgtMesh
6485
6486   smIdType idShift = meshDS->MaxNodeID();
6487   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6488     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6489     {
6490 #ifndef _DEBUG_
6491       if ( n->NbInverseElements() > 0 )
6492 #endif
6493       {
6494         const SMDS_MeshNode* n2 =
6495           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6496         myLastCreatedNodes.push_back( n2 );
6497         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6498       }
6499     }
6500
6501   ElemFeatures elemType;
6502   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6503     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6504     {
6505       elemType.Init( f );
6506       elemType.myNodes.clear();
6507       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6508       {
6509         const SMDS_MeshNode* n2 = nIt->next();
6510         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6511       }
6512       tgtEditor.AddElement( elemType.myNodes, elemType );
6513       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6514     }
6515
6516   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6517
6518   PGroupIDs newGroupIDs;
6519   if ( theMakeGroups )
6520     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6521   else
6522     newGroupIDs.reset( new std::list< int > );
6523
6524   return newGroupIDs;
6525 }
6526
6527 //=======================================================================
6528 /*!
6529  * \brief Create groups of elements made during transformation
6530  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6531  *  \param elemGens - elements making corresponding myLastCreatedElems
6532  *  \param postfix - to push_back to names of new groups
6533  *  \param targetMesh - mesh to create groups in
6534  *  \param topPresent - is there are "top" elements that are created by sweeping
6535  */
6536 //=======================================================================
6537
6538 SMESH_MeshEditor::PGroupIDs
6539 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6540                                  const SMESH_SequenceOfElemPtr& elemGens,
6541                                  const std::string&             postfix,
6542                                  SMESH_Mesh*                    targetMesh,
6543                                  const bool                     topPresent)
6544 {
6545   PGroupIDs newGroupIDs( new list<int> );
6546   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6547
6548   // Sort existing groups by types and collect their names
6549
6550   // containers to store an old group and generated new ones;
6551   // 1st new group is for result elems of different type than a source one;
6552   // 2nd new group is for same type result elems ("top" group at extrusion)
6553   using boost::tuple;
6554   using boost::make_tuple;
6555   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6556   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6557   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6558   // group names
6559   set< string > groupNames;
6560
6561   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6562   if ( !groupIt->more() ) return newGroupIDs;
6563
6564   int newGroupID = mesh->GetGroupIds().back()+1;
6565   while ( groupIt->more() )
6566   {
6567     SMESH_Group * group = groupIt->next();
6568     if ( !group ) continue;
6569     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6570     if ( !groupDS || groupDS->IsEmpty() ) continue;
6571     groupNames.insert    ( group->GetName() );
6572     groupDS->SetStoreName( group->GetName() );
6573     const SMDSAbs_ElementType type = groupDS->GetType();
6574     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6575     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6576     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6577     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6578   }
6579
6580   // Loop on nodes and elements to add them in new groups
6581
6582   vector< const SMDS_MeshElement* > resultElems;
6583   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6584   {
6585     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6586     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6587     if ( gens.size() != elems.size() )
6588       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6589
6590     // loop on created elements
6591     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6592     {
6593       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6594       if ( !sourceElem ) {
6595         MESSAGE("generateGroups(): NULL source element");
6596         continue;
6597       }
6598       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6599       if ( groupsOldNew.empty() ) { // no groups of this type at all
6600         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6601           ++iElem; // skip all elements made by sourceElem
6602         continue;
6603       }
6604       // collect all elements made by the iElem-th sourceElem
6605       resultElems.clear();
6606       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6607         if ( resElem != sourceElem )
6608           resultElems.push_back( resElem );
6609       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6610         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6611           if ( resElem != sourceElem )
6612             resultElems.push_back( resElem );
6613
6614       const SMDS_MeshElement* topElem = 0;
6615       if ( isNodes ) // there must be a top element
6616       {
6617         topElem = resultElems.back();
6618         resultElems.pop_back();
6619       }
6620       else
6621       {
6622         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6623         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6624           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6625           {
6626             topElem = *resElemIt;
6627             *resElemIt = 0; // erase *resElemIt
6628             break;
6629           }
6630       }
6631       // add resultElems to groups originted from ones the sourceElem belongs to
6632       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6633       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6634       {
6635         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6636         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6637         {
6638           // fill in a new group
6639           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6640           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6641           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6642             if ( *resElemIt )
6643               newGroup.Add( *resElemIt );
6644
6645           // fill a "top" group
6646           if ( topElem )
6647           {
6648             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6649             newTopGroup.Add( topElem );
6650           }
6651         }
6652       }
6653     } // loop on created elements
6654   }// loop on nodes and elements
6655
6656   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6657
6658   list<int> topGrouIds;
6659   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6660   {
6661     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6662     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6663                                       orderedOldNewGroups[i]->get<2>() };
6664     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6665     {
6666       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6667       if ( newGroupDS->IsEmpty() )
6668       {
6669         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6670       }
6671       else
6672       {
6673         // set group type
6674         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6675
6676         // make a name
6677         const bool isTop = ( topPresent &&
6678                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6679                              is2nd );
6680
6681         string name = oldGroupDS->GetStoreName();
6682         { // remove trailing whitespaces (issue 22599)
6683           size_t size = name.size();
6684           while ( size > 1 && isspace( name[ size-1 ]))
6685             --size;
6686           if ( size != name.size() )
6687           {
6688             name.resize( size );
6689             oldGroupDS->SetStoreName( name.c_str() );
6690           }
6691         }
6692         if ( !targetMesh ) {
6693           string suffix = ( isTop ? "top": postfix.c_str() );
6694           name += "_";
6695           name += suffix;
6696           int nb = 1;
6697           while ( !groupNames.insert( name ).second ) // name exists
6698             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6699         }
6700         else if ( isTop ) {
6701           name += "_top";
6702         }
6703         newGroupDS->SetStoreName( name.c_str() );
6704
6705         // make a SMESH_Groups
6706         mesh->AddGroup( newGroupDS );
6707         if ( isTop )
6708           topGrouIds.push_back( newGroupDS->GetID() );
6709         else
6710           newGroupIDs->push_back( newGroupDS->GetID() );
6711       }
6712     }
6713   }
6714   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6715
6716   return newGroupIDs;
6717 }
6718
6719 //================================================================================
6720 /*!
6721  *  * \brief Return list of group of nodes close to each other within theTolerance
6722  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6723  *  *        an Octree algorithm
6724  *  \param [in,out] theNodes - the nodes to treat
6725  *  \param [in]     theTolerance - the tolerance
6726  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6727  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6728  *         corner and medium nodes in separate groups
6729  */
6730 //================================================================================
6731
6732 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6733                                             const double         theTolerance,
6734                                             TListOfListOfNodes & theGroupsOfNodes,
6735                                             bool                 theSeparateCornersAndMedium)
6736 {
6737   ClearLastCreated();
6738
6739   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6740        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6741        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6742     theSeparateCornersAndMedium = false;
6743
6744   TIDSortedNodeSet& corners = theNodes;
6745   TIDSortedNodeSet  medium;
6746
6747   if ( theNodes.empty() ) // get all nodes in the mesh
6748   {
6749     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6750     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6751     if ( theSeparateCornersAndMedium )
6752       while ( nIt->more() )
6753       {
6754         const SMDS_MeshNode* n = nIt->next();
6755         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6756         nodeSet->insert( nodeSet->end(), n );
6757       }
6758     else
6759       while ( nIt->more() )
6760         theNodes.insert( theNodes.end(), nIt->next() );
6761   }
6762   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6763   {
6764     TIDSortedNodeSet::iterator nIt = corners.begin();
6765     while ( nIt != corners.end() )
6766       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6767       {
6768         medium.insert( medium.end(), *nIt );
6769         corners.erase( nIt++ );
6770       }
6771       else
6772       {
6773         ++nIt;
6774       }
6775   }
6776
6777   if ( !corners.empty() )
6778     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6779   if ( !medium.empty() )
6780     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6781 }
6782
6783 //=======================================================================
6784 //function : SimplifyFace
6785 //purpose  : split a chain of nodes into several closed chains
6786 //=======================================================================
6787
6788 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6789                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6790                                     vector<int>&                         quantities) const
6791 {
6792   int nbNodes = faceNodes.size();
6793   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6794     --nbNodes;
6795   if ( nbNodes < 3 )
6796     return 0;
6797   size_t prevNbQuant = quantities.size();
6798
6799   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6800   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6801   map< const SMDS_MeshNode*, int >::iterator nInd;
6802
6803   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6804   simpleNodes.push_back( faceNodes[0] );
6805   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6806   {
6807     if ( faceNodes[ iCur ] != simpleNodes.back() )
6808     {
6809       int index = simpleNodes.size();
6810       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6811       int prevIndex = nInd->second;
6812       if ( prevIndex < index )
6813       {
6814         // a sub-loop found
6815         int loopLen = index - prevIndex;
6816         if ( loopLen > 2 )
6817         {
6818           // store the sub-loop
6819           quantities.push_back( loopLen );
6820           for ( int i = prevIndex; i < index; i++ )
6821             poly_nodes.push_back( simpleNodes[ i ]);
6822         }
6823         simpleNodes.resize( prevIndex+1 );
6824       }
6825       else
6826       {
6827         simpleNodes.push_back( faceNodes[ iCur ]);
6828       }
6829     }
6830   }
6831
6832   if ( simpleNodes.size() > 2 )
6833   {
6834     quantities.push_back( simpleNodes.size() );
6835     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6836   }
6837
6838   return quantities.size() - prevNbQuant;
6839 }
6840
6841 //=======================================================================
6842 //function : MergeNodes
6843 //purpose  : In each group, the cdr of nodes are substituted by the first one
6844 //           in all elements.
6845 //=======================================================================
6846
6847 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6848                                    const bool           theAvoidMakingHoles)
6849 {
6850   ClearLastCreated();
6851
6852   SMESHDS_Mesh* mesh = GetMeshDS();
6853
6854   TNodeNodeMap nodeNodeMap; // node to replace - new node
6855   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6856   list< smIdType > rmElemIds, rmNodeIds;
6857   vector< ElemFeatures > newElemDefs;
6858
6859   // Fill nodeNodeMap and elems
6860
6861   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6862   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6863   {
6864     list<const SMDS_MeshNode*>& nodes = *grIt;
6865     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6866     const SMDS_MeshNode* nToKeep = *nIt;
6867     for ( ++nIt; nIt != nodes.end(); nIt++ )
6868     {
6869       const SMDS_MeshNode* nToRemove = *nIt;
6870       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6871       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6872       while ( invElemIt->more() ) {
6873         const SMDS_MeshElement* elem = invElemIt->next();
6874         elems.insert(elem);
6875       }
6876     }
6877   }
6878
6879   // Apply recursive replacements (BUG 0020185)
6880   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6881   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6882   {
6883     const SMDS_MeshNode* nToKeep = nnIt->second;
6884     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6885     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6886     {
6887       nToKeep = nnIt_i->second;
6888       nnIt->second = nToKeep;
6889       nnIt_i = nodeNodeMap.find( nToKeep );
6890     }
6891   }
6892
6893   if ( theAvoidMakingHoles )
6894   {
6895     // find elements whose topology changes
6896
6897     vector<const SMDS_MeshElement*> pbElems;
6898     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6899     for ( ; eIt != elems.end(); ++eIt )
6900     {
6901       const SMDS_MeshElement* elem = *eIt;
6902       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6903       while ( itN->more() )
6904       {
6905         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6906         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6907         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6908         {
6909           // several nodes of elem stick
6910           pbElems.push_back( elem );
6911           break;
6912         }
6913       }
6914     }
6915     // exclude from merge nodes causing spoiling element
6916     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6917     {
6918       bool nodesExcluded = false;
6919       for ( size_t i = 0; i < pbElems.size(); ++i )
6920       {
6921         size_t prevNbMergeNodes = nodeNodeMap.size();
6922         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6923              prevNbMergeNodes < nodeNodeMap.size() )
6924           nodesExcluded = true;
6925       }
6926       if ( !nodesExcluded )
6927         break;
6928     }
6929   }
6930
6931   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6932   {
6933     const SMDS_MeshNode* nToRemove = nnIt->first;
6934     const SMDS_MeshNode* nToKeep   = nnIt->second;
6935     if ( nToRemove != nToKeep )
6936     {
6937       rmNodeIds.push_back( nToRemove->GetID() );
6938       AddToSameGroups( nToKeep, nToRemove, mesh );
6939       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6940       // w/o creating node in place of merged ones.
6941       SMDS_PositionPtr pos = nToRemove->GetPosition();
6942       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6943         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6944           sm->SetIsAlwaysComputed( true );
6945     }
6946   }
6947
6948   // Change element nodes or remove an element
6949
6950   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6951   for ( ; eIt != elems.end(); eIt++ )
6952   {
6953     const SMDS_MeshElement* elem = *eIt;
6954     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6955
6956     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6957     if ( !keepElem )
6958       rmElemIds.push_back( elem->GetID() );
6959
6960     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6961     {
6962       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6963                                                & newElemDefs[i].myNodes[0],
6964                                                newElemDefs[i].myNodes.size() ))
6965       {
6966         if ( i == 0 )
6967         {
6968           newElemDefs[i].SetID( elem->GetID() );
6969           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6970           if ( !keepElem ) rmElemIds.pop_back();
6971         }
6972         else
6973         {
6974           newElemDefs[i].SetID( -1 );
6975         }
6976         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6977         if ( sm && newElem )
6978           sm->AddElement( newElem );
6979         if ( elem != newElem )
6980           ReplaceElemInGroups( elem, newElem, mesh );
6981       }
6982     }
6983   }
6984
6985   // Remove bad elements, then equal nodes (order important)
6986   Remove( rmElemIds, /*isNodes=*/false );
6987   Remove( rmNodeIds, /*isNodes=*/true );
6988
6989   return;
6990 }
6991
6992 //=======================================================================
6993 //function : applyMerge
6994 //purpose  : Compute new connectivity of an element after merging nodes
6995 //  \param [in] elems - the element
6996 //  \param [out] newElemDefs - definition(s) of result element(s)
6997 //  \param [inout] nodeNodeMap - nodes to merge
6998 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6999 //              after merging (but not degenerated), removes nodes causing
7000 //              the invalidity from \a nodeNodeMap.
7001 //  \return bool - true if the element should be removed
7002 //=======================================================================
7003
7004 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7005                                    vector< ElemFeatures >& newElemDefs,
7006                                    TNodeNodeMap&           nodeNodeMap,
7007                                    const bool              avoidMakingHoles )
7008 {
7009   bool toRemove = false; // to remove elem
7010   int nbResElems = 1;    // nb new elements
7011
7012   newElemDefs.resize(nbResElems);
7013   newElemDefs[0].Init( elem );
7014   newElemDefs[0].myNodes.clear();
7015
7016   set<const SMDS_MeshNode*> nodeSet;
7017   vector< const SMDS_MeshNode*>   curNodes;
7018   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7019   vector<int> iRepl;
7020
7021   const        int  nbNodes = elem->NbNodes();
7022   SMDSAbs_EntityType entity = elem->GetEntityType();
7023
7024   curNodes.resize( nbNodes );
7025   uniqueNodes.resize( nbNodes );
7026   iRepl.resize( nbNodes );
7027   int iUnique = 0, iCur = 0, nbRepl = 0;
7028
7029   // Get new seq of nodes
7030
7031   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7032   while ( itN->more() )
7033   {
7034     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7035
7036     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7037     if ( nnIt != nodeNodeMap.end() ) {
7038       n = (*nnIt).second;
7039     }
7040     curNodes[ iCur ] = n;
7041     bool isUnique = nodeSet.insert( n ).second;
7042     if ( isUnique )
7043       uniqueNodes[ iUnique++ ] = n;
7044     else
7045       iRepl[ nbRepl++ ] = iCur;
7046     iCur++;
7047   }
7048
7049   // Analyse element topology after replacement
7050
7051   int nbUniqueNodes = nodeSet.size();
7052   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7053   {
7054     toRemove = true;
7055     nbResElems = 0;
7056
7057     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7058     {
7059       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7060       int nbCorners = nbNodes / 2;
7061       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7062       {
7063         int iNext = ( iCur + 1 ) % nbCorners;
7064         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7065         {
7066           int iMedium = iCur + nbCorners;
7067           vector< const SMDS_MeshNode* >::iterator i =
7068             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7069                        uniqueNodes.end(),
7070                        curNodes[ iMedium ]);
7071           if ( i != uniqueNodes.end() )
7072           {
7073             --nbUniqueNodes;
7074             for ( ; i+1 != uniqueNodes.end(); ++i )
7075               *i = *(i+1);
7076           }
7077         }
7078       }
7079     }
7080
7081     switch ( entity )
7082     {
7083     case SMDSEntity_Polygon:
7084     case SMDSEntity_Quad_Polygon: // Polygon
7085     {
7086       ElemFeatures* elemType = & newElemDefs[0];
7087       const bool isQuad = elemType->myIsQuad;
7088       if ( isQuad )
7089         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7090           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7091
7092       // a polygon can divide into several elements
7093       vector<const SMDS_MeshNode *> polygons_nodes;
7094       vector<int> quantities;
7095       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7096       newElemDefs.resize( nbResElems );
7097       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7098       {
7099         ElemFeatures* elemType = & newElemDefs[iface];
7100         if ( iface ) elemType->Init( elem );
7101
7102         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7103         int nbNewNodes = quantities[iface];
7104         face_nodes.assign( polygons_nodes.begin() + inode,
7105                            polygons_nodes.begin() + inode + nbNewNodes );
7106         inode += nbNewNodes;
7107         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7108         {
7109           bool isValid = ( nbNewNodes % 2 == 0 );
7110           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7111             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7112           elemType->SetQuad( isValid );
7113           if ( isValid ) // put medium nodes after corners
7114             SMDS_MeshCell::applyInterlaceRev
7115               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7116                                                     nbNewNodes ), face_nodes );
7117         }
7118         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7119       }
7120       nbUniqueNodes = newElemDefs[0].myNodes.size();
7121       break;
7122     } // Polygon
7123
7124     case SMDSEntity_Polyhedra: // Polyhedral volume
7125     {
7126       if ( nbUniqueNodes >= 4 )
7127       {
7128         // each face has to be analyzed in order to check volume validity
7129         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7130         {
7131           int nbFaces = aPolyedre->NbFaces();
7132
7133           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7134           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7135           vector<const SMDS_MeshNode *>  faceNodes;
7136           poly_nodes.clear();
7137           quantities.clear();
7138
7139           for (int iface = 1; iface <= nbFaces; iface++)
7140           {
7141             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7142             faceNodes.resize( nbFaceNodes );
7143             for (int inode = 1; inode <= nbFaceNodes; inode++)
7144             {
7145               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7146               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7147               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7148                 faceNode = (*nnIt).second;
7149               faceNodes[inode - 1] = faceNode;
7150             }
7151             SimplifyFace(faceNodes, poly_nodes, quantities);
7152           }
7153
7154           if ( quantities.size() > 3 )
7155           {
7156             // TODO: remove coincident faces
7157             nbResElems = 1;
7158             nbUniqueNodes = newElemDefs[0].myNodes.size();
7159           }
7160         }
7161       }
7162     }
7163     break;
7164
7165     // Regular elements
7166     // TODO not all the possible cases are solved. Find something more generic?
7167     case SMDSEntity_Edge: //////// EDGE
7168     case SMDSEntity_Triangle: //// TRIANGLE
7169     case SMDSEntity_Quad_Triangle:
7170     case SMDSEntity_Tetra:
7171     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7172     {
7173       break;
7174     }
7175     case SMDSEntity_Quad_Edge:
7176     {
7177       break;
7178     }
7179     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7180     {
7181       if ( nbUniqueNodes < 3 )
7182         toRemove = true;
7183       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7184         toRemove = true; // opposite nodes stick
7185       else
7186         toRemove = false;
7187       break;
7188     }
7189     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7190     {
7191       //   1    5    2
7192       //    +---+---+
7193       //    |       |
7194       //   4+       +6
7195       //    |       |
7196       //    +---+---+
7197       //   0    7    3
7198       if ( nbUniqueNodes == 6 &&
7199            iRepl[0] < 4       &&
7200            ( nbRepl == 1 || iRepl[1] >= 4 ))
7201       {
7202         toRemove = false;
7203       }
7204       break;
7205     }
7206     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7207     {
7208       //   1    5    2
7209       //    +---+---+
7210       //    |       |
7211       //   4+  8+   +6
7212       //    |       |
7213       //    +---+---+
7214       //   0    7    3
7215       if ( nbUniqueNodes == 7 &&
7216            iRepl[0] < 4       &&
7217            ( nbRepl == 1 || iRepl[1] != 8 ))
7218       {
7219         toRemove = false;
7220       }
7221       break;
7222     }
7223     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7224     {
7225       if ( nbUniqueNodes == 4 ) {
7226         // ---------------------------------> tetrahedron
7227         if ( curNodes[3] == curNodes[4] &&
7228              curNodes[3] == curNodes[5] ) {
7229           // top nodes stick
7230           toRemove = false;
7231         }
7232         else if ( curNodes[0] == curNodes[1] &&
7233                   curNodes[0] == curNodes[2] ) {
7234           // bottom nodes stick: set a top before
7235           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7236           uniqueNodes[ 0 ] = curNodes [ 5 ];
7237           uniqueNodes[ 1 ] = curNodes [ 4 ];
7238           uniqueNodes[ 2 ] = curNodes [ 3 ];
7239           toRemove = false;
7240         }
7241         else if (( curNodes[0] == curNodes[3] ) +
7242                  ( curNodes[1] == curNodes[4] ) +
7243                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7244           // a lateral face turns into a line
7245           toRemove = false;
7246         }
7247       }
7248       else if ( nbUniqueNodes == 5 ) {
7249         // PENTAHEDRON --------------------> pyramid
7250         if ( curNodes[0] == curNodes[3] )
7251         {
7252           uniqueNodes[ 0 ] = curNodes[ 1 ];
7253           uniqueNodes[ 1 ] = curNodes[ 4 ];
7254           uniqueNodes[ 2 ] = curNodes[ 5 ];
7255           uniqueNodes[ 3 ] = curNodes[ 2 ];
7256           uniqueNodes[ 4 ] = curNodes[ 0 ];
7257           toRemove = false;
7258         }
7259         if ( curNodes[1] == curNodes[4] )
7260         {
7261           uniqueNodes[ 0 ] = curNodes[ 0 ];
7262           uniqueNodes[ 1 ] = curNodes[ 2 ];
7263           uniqueNodes[ 2 ] = curNodes[ 5 ];
7264           uniqueNodes[ 3 ] = curNodes[ 3 ];
7265           uniqueNodes[ 4 ] = curNodes[ 1 ];
7266           toRemove = false;
7267         }
7268         if ( curNodes[2] == curNodes[5] )
7269         {
7270           uniqueNodes[ 0 ] = curNodes[ 0 ];
7271           uniqueNodes[ 1 ] = curNodes[ 3 ];
7272           uniqueNodes[ 2 ] = curNodes[ 4 ];
7273           uniqueNodes[ 3 ] = curNodes[ 1 ];
7274           uniqueNodes[ 4 ] = curNodes[ 2 ];
7275           toRemove = false;
7276         }
7277       }
7278       break;
7279     }
7280     case SMDSEntity_Hexa:
7281     {
7282       //////////////////////////////////// HEXAHEDRON
7283       SMDS_VolumeTool hexa (elem);
7284       hexa.SetExternalNormal();
7285       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7286         //////////////////////// HEX ---> tetrahedron
7287         for ( int iFace = 0; iFace < 6; iFace++ ) {
7288           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7289           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7290               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7291               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7292             // one face turns into a point ...
7293             int  pickInd = ind[ 0 ];
7294             int iOppFace = hexa.GetOppFaceIndex( iFace );
7295             ind = hexa.GetFaceNodesIndices( iOppFace );
7296             int nbStick = 0;
7297             uniqueNodes.clear();
7298             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7299               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7300                 nbStick++;
7301               else
7302                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7303             }
7304             if ( nbStick == 1 ) {
7305               // ... and the opposite one - into a triangle.
7306               // set a top node
7307               uniqueNodes.push_back( curNodes[ pickInd ]);
7308               toRemove = false;
7309             }
7310             break;
7311           }
7312         }
7313       }
7314       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7315         //////////////////////// HEX ---> prism
7316         int nbTria = 0, iTria[3];
7317         const int *ind; // indices of face nodes
7318         // look for triangular faces
7319         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7320           ind = hexa.GetFaceNodesIndices( iFace );
7321           TIDSortedNodeSet faceNodes;
7322           for ( iCur = 0; iCur < 4; iCur++ )
7323             faceNodes.insert( curNodes[ind[iCur]] );
7324           if ( faceNodes.size() == 3 )
7325             iTria[ nbTria++ ] = iFace;
7326         }
7327         // check if triangles are opposite
7328         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7329         {
7330           // set nodes of the bottom triangle
7331           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7332           vector<int> indB;
7333           for ( iCur = 0; iCur < 4; iCur++ )
7334             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7335               indB.push_back( ind[iCur] );
7336           if ( !hexa.IsForward() )
7337             std::swap( indB[0], indB[2] );
7338           for ( iCur = 0; iCur < 3; iCur++ )
7339             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7340           // set nodes of the top triangle
7341           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7342           for ( iCur = 0; iCur < 3; ++iCur )
7343             for ( int j = 0; j < 4; ++j )
7344               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7345               {
7346                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7347                 break;
7348               }
7349           toRemove = false;
7350           break;
7351         }
7352       }
7353       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7354         //////////////////// HEXAHEDRON ---> pyramid
7355         for ( int iFace = 0; iFace < 6; iFace++ ) {
7356           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7357           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7358               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7359               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7360             // one face turns into a point ...
7361             int iOppFace = hexa.GetOppFaceIndex( iFace );
7362             ind = hexa.GetFaceNodesIndices( iOppFace );
7363             uniqueNodes.clear();
7364             for ( iCur = 0; iCur < 4; iCur++ ) {
7365               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7366                 break;
7367               else
7368                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7369             }
7370             if ( uniqueNodes.size() == 4 ) {
7371               // ... and the opposite one is a quadrangle
7372               // set a top node
7373               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7374               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7375               toRemove = false;
7376             }
7377             break;
7378           }
7379         }
7380       }
7381
7382       if ( toRemove && nbUniqueNodes > 4 ) {
7383         ////////////////// HEXAHEDRON ---> polyhedron
7384         hexa.SetExternalNormal();
7385         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7386         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7387         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7388         quantities.reserve( 6 );     quantities.clear();
7389         for ( int iFace = 0; iFace < 6; iFace++ )
7390         {
7391           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7392           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7393                curNodes[ind[1]] == curNodes[ind[3]] )
7394           {
7395             quantities.clear();
7396             break; // opposite nodes stick
7397           }
7398           nodeSet.clear();
7399           for ( iCur = 0; iCur < 4; iCur++ )
7400           {
7401             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7402               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7403           }
7404           if ( nodeSet.size() < 3 )
7405             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7406           else
7407             quantities.push_back( nodeSet.size() );
7408         }
7409         if ( quantities.size() >= 4 )
7410         {
7411           nbResElems = 1;
7412           nbUniqueNodes = poly_nodes.size();
7413           newElemDefs[0].SetPoly(true);
7414         }
7415       }
7416       break;
7417     } // case HEXAHEDRON
7418
7419     default:
7420       toRemove = true;
7421
7422     } // switch ( entity )
7423
7424     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7425     {
7426       // erase from nodeNodeMap nodes whose merge spoils elem
7427       vector< const SMDS_MeshNode* > noMergeNodes;
7428       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7429       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7430         nodeNodeMap.erase( noMergeNodes[i] );
7431     }
7432     
7433   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7434
7435   uniqueNodes.resize( nbUniqueNodes );
7436
7437   if ( !toRemove && nbResElems == 0 )
7438     nbResElems = 1;
7439
7440   newElemDefs.resize( nbResElems );
7441
7442   return !toRemove;
7443 }
7444
7445
7446 // ========================================================
7447 // class   : ComparableElement
7448 // purpose : allow comparing elements basing on their nodes
7449 // ========================================================
7450
7451 class ComparableElement : public boost::container::flat_set< smIdType >
7452 {
7453   typedef boost::container::flat_set< smIdType >  int_set;
7454
7455   const SMDS_MeshElement* myElem;
7456   smIdType                mySumID;
7457   mutable int             myGroupID;
7458
7459 public:
7460
7461   ComparableElement( const SMDS_MeshElement* theElem ):
7462     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7463   {
7464     this->reserve( theElem->NbNodes() );
7465     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7466     {
7467       smIdType id = nodeIt->next()->GetID();
7468       mySumID += id;
7469       this->insert( id );
7470     }
7471   }
7472
7473   const SMDS_MeshElement* GetElem() const { return myElem; }
7474
7475   int& GroupID() const { return myGroupID; }
7476   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7477
7478   ComparableElement( const ComparableElement& theSource ) // move copy
7479     : int_set()
7480   {
7481     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7482     (int_set&) (*this ) = std::move( src );
7483     myElem    = src.myElem;
7484     mySumID   = src.mySumID;
7485     myGroupID = src.myGroupID;
7486   }
7487
7488   static int HashCode(const ComparableElement& se, int limit )
7489   {
7490     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7491   }
7492   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7493   {
7494     return ( se1 == se2 );
7495   }
7496
7497 };
7498
7499 //=======================================================================
7500 //function : FindEqualElements
7501 //purpose  : Return list of group of elements built on the same nodes.
7502 //           Search among theElements or in the whole mesh if theElements is empty
7503 //=======================================================================
7504
7505 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7506                                           TListOfListOfElementsID & theGroupsOfElementsID )
7507 {
7508   ClearLastCreated();
7509
7510   SMDS_ElemIteratorPtr elemIt;
7511   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7512   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7513
7514   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7515   typedef std::list<smIdType>                                     TGroupOfElems;
7516   TMapOfElements               mapOfElements;
7517   std::vector< TGroupOfElems > arrayOfGroups;
7518   TGroupOfElems                groupOfElems;
7519
7520   while ( elemIt->more() )
7521   {
7522     const SMDS_MeshElement* curElem = elemIt->next();
7523     if ( curElem->IsNull() )
7524       continue;
7525     ComparableElement      compElem = curElem;
7526     // check uniqueness
7527     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7528     if ( elemInSet.GetElem() != curElem ) // coincident elem
7529     {
7530       int& iG = elemInSet.GroupID();
7531       if ( iG < 0 )
7532       {
7533         iG = arrayOfGroups.size();
7534         arrayOfGroups.push_back( groupOfElems );
7535         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7536       }
7537       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7538     }
7539   }
7540
7541   groupOfElems.clear();
7542   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7543   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7544   {
7545     if ( groupIt->size() > 1 ) {
7546       //groupOfElems.sort(); -- theElements are sorted already
7547       theGroupsOfElementsID.emplace_back( *groupIt );
7548     }
7549   }
7550 }
7551
7552 //=======================================================================
7553 //function : MergeElements
7554 //purpose  : In each given group, substitute all elements by the first one.
7555 //=======================================================================
7556
7557 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7558 {
7559   ClearLastCreated();
7560
7561   typedef list<smIdType> TListOfIDs;
7562   TListOfIDs rmElemIds; // IDs of elems to remove
7563
7564   SMESHDS_Mesh* aMesh = GetMeshDS();
7565
7566   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7567   while ( groupsIt != theGroupsOfElementsID.end() ) {
7568     TListOfIDs& aGroupOfElemID = *groupsIt;
7569     aGroupOfElemID.sort();
7570     int elemIDToKeep = aGroupOfElemID.front();
7571     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7572     aGroupOfElemID.pop_front();
7573     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7574     while ( idIt != aGroupOfElemID.end() ) {
7575       int elemIDToRemove = *idIt;
7576       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7577       // add the kept element in groups of removed one (PAL15188)
7578       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7579       rmElemIds.push_back( elemIDToRemove );
7580       ++idIt;
7581     }
7582     ++groupsIt;
7583   }
7584
7585   Remove( rmElemIds, false );
7586 }
7587
7588 //=======================================================================
7589 //function : MergeEqualElements
7590 //purpose  : Remove all but one of elements built on the same nodes.
7591 //=======================================================================
7592
7593 void SMESH_MeshEditor::MergeEqualElements()
7594 {
7595   TIDSortedElemSet aMeshElements; /* empty input ==
7596                                      to merge equal elements in the whole mesh */
7597   TListOfListOfElementsID aGroupsOfElementsID;
7598   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7599   MergeElements( aGroupsOfElementsID );
7600 }
7601
7602 //=======================================================================
7603 //function : findAdjacentFace
7604 //purpose  :
7605 //=======================================================================
7606
7607 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7608                                                 const SMDS_MeshNode* n2,
7609                                                 const SMDS_MeshElement* elem)
7610 {
7611   TIDSortedElemSet elemSet, avoidSet;
7612   if ( elem )
7613     avoidSet.insert ( elem );
7614   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7615 }
7616
7617 //=======================================================================
7618 //function : findSegment
7619 //purpose  : Return a mesh segment by two nodes one of which can be medium
7620 //=======================================================================
7621
7622 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7623                                            const SMDS_MeshNode* n2)
7624 {
7625   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7626   while ( it->more() )
7627   {
7628     const SMDS_MeshElement* seg = it->next();
7629     if ( seg->GetNodeIndex( n2 ) >= 0 )
7630       return seg;
7631   }
7632   return 0;
7633 }
7634
7635 //=======================================================================
7636 //function : FindFreeBorder
7637 //purpose  :
7638 //=======================================================================
7639
7640 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7641
7642 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7643                                        const SMDS_MeshNode*             theSecondNode,
7644                                        const SMDS_MeshNode*             theLastNode,
7645                                        list< const SMDS_MeshNode* > &   theNodes,
7646                                        list< const SMDS_MeshElement* >& theFaces)
7647 {
7648   if ( !theFirstNode || !theSecondNode )
7649     return false;
7650   // find border face between theFirstNode and theSecondNode
7651   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7652   if ( !curElem )
7653     return false;
7654
7655   theFaces.push_back( curElem );
7656   theNodes.push_back( theFirstNode );
7657   theNodes.push_back( theSecondNode );
7658
7659   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7660   //TIDSortedElemSet foundElems;
7661   bool needTheLast = ( theLastNode != 0 );
7662
7663   vector<const SMDS_MeshNode*> nodes;
7664   
7665   while ( nStart != theLastNode ) {
7666     if ( nStart == theFirstNode )
7667       return !needTheLast;
7668
7669     // find all free border faces sharing nStart
7670
7671     list< const SMDS_MeshElement* > curElemList;
7672     list< const SMDS_MeshNode* >    nStartList;
7673     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7674     while ( invElemIt->more() ) {
7675       const SMDS_MeshElement* e = invElemIt->next();
7676       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7677       {
7678         // get nodes
7679         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7680                       SMDS_MeshElement::iterator() );
7681         nodes.push_back( nodes[ 0 ]);
7682
7683         // check 2 links
7684         int iNode = 0, nbNodes = nodes.size() - 1;
7685         for ( iNode = 0; iNode < nbNodes; iNode++ )
7686           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7687                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7688               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7689           {
7690             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7691             curElemList.push_back( e );
7692           }
7693       }
7694     }
7695     // analyse the found
7696
7697     int nbNewBorders = curElemList.size();
7698     if ( nbNewBorders == 0 ) {
7699       // no free border furthermore
7700       return !needTheLast;
7701     }
7702     else if ( nbNewBorders == 1 ) {
7703       // one more element found
7704       nIgnore = nStart;
7705       nStart = nStartList.front();
7706       curElem = curElemList.front();
7707       theFaces.push_back( curElem );
7708       theNodes.push_back( nStart );
7709     }
7710     else {
7711       // several continuations found
7712       list< const SMDS_MeshElement* >::iterator curElemIt;
7713       list< const SMDS_MeshNode* >::iterator nStartIt;
7714       // check if one of them reached the last node
7715       if ( needTheLast ) {
7716         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7717              curElemIt!= curElemList.end();
7718              curElemIt++, nStartIt++ )
7719           if ( *nStartIt == theLastNode ) {
7720             theFaces.push_back( *curElemIt );
7721             theNodes.push_back( *nStartIt );
7722             return true;
7723           }
7724       }
7725       // find the best free border by the continuations
7726       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7727       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7728       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7729            curElemIt!= curElemList.end();
7730            curElemIt++, nStartIt++ )
7731       {
7732         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7733         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7734         // find one more free border
7735         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7736           cNL->clear();
7737           cFL->clear();
7738         }
7739         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7740           // choice: clear a worse one
7741           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7742           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7743           contNodes[ iWorse ].clear();
7744           contFaces[ iWorse ].clear();
7745         }
7746       }
7747       if ( contNodes[0].empty() && contNodes[1].empty() )
7748         return false;
7749
7750       // push_back the best free border
7751       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7752       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7753       //theNodes.pop_back(); // remove nIgnore
7754       theNodes.pop_back(); // remove nStart
7755       //theFaces.pop_back(); // remove curElem
7756       theNodes.splice( theNodes.end(), *cNL );
7757       theFaces.splice( theFaces.end(), *cFL );
7758       return true;
7759
7760     } // several continuations found
7761   } // while ( nStart != theLastNode )
7762
7763   return true;
7764 }
7765
7766 //=======================================================================
7767 //function : CheckFreeBorderNodes
7768 //purpose  : Return true if the tree nodes are on a free border
7769 //=======================================================================
7770
7771 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7772                                             const SMDS_MeshNode* theNode2,
7773                                             const SMDS_MeshNode* theNode3)
7774 {
7775   list< const SMDS_MeshNode* > nodes;
7776   list< const SMDS_MeshElement* > faces;
7777   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7778 }
7779
7780 //=======================================================================
7781 //function : SewFreeBorder
7782 //purpose  :
7783 //warning  : for border-to-side sewing theSideSecondNode is considered as
7784 //           the last side node and theSideThirdNode is not used
7785 //=======================================================================
7786
7787 SMESH_MeshEditor::Sew_Error
7788 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7789                                  const SMDS_MeshNode* theBordSecondNode,
7790                                  const SMDS_MeshNode* theBordLastNode,
7791                                  const SMDS_MeshNode* theSideFirstNode,
7792                                  const SMDS_MeshNode* theSideSecondNode,
7793                                  const SMDS_MeshNode* theSideThirdNode,
7794                                  const bool           theSideIsFreeBorder,
7795                                  const bool           toCreatePolygons,
7796                                  const bool           toCreatePolyedrs)
7797 {
7798   ClearLastCreated();
7799
7800   Sew_Error aResult = SEW_OK;
7801
7802   // ====================================
7803   //    find side nodes and elements
7804   // ====================================
7805
7806   list< const SMDS_MeshNode* >    nSide[ 2 ];
7807   list< const SMDS_MeshElement* > eSide[ 2 ];
7808   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7809   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7810
7811   // Free border 1
7812   // --------------
7813   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7814                       nSide[0], eSide[0])) {
7815     MESSAGE(" Free Border 1 not found " );
7816     aResult = SEW_BORDER1_NOT_FOUND;
7817   }
7818   if (theSideIsFreeBorder) {
7819     // Free border 2
7820     // --------------
7821     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7822                         nSide[1], eSide[1])) {
7823       MESSAGE(" Free Border 2 not found " );
7824       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7825     }
7826   }
7827   if ( aResult != SEW_OK )
7828     return aResult;
7829
7830   if (!theSideIsFreeBorder) {
7831     // Side 2
7832     // --------------
7833
7834     // -------------------------------------------------------------------------
7835     // Algo:
7836     // 1. If nodes to merge are not coincident, move nodes of the free border
7837     //    from the coord sys defined by the direction from the first to last
7838     //    nodes of the border to the correspondent sys of the side 2
7839     // 2. On the side 2, find the links most co-directed with the correspondent
7840     //    links of the free border
7841     // -------------------------------------------------------------------------
7842
7843     // 1. Since sewing may break if there are volumes to split on the side 2,
7844     //    we won't move nodes but just compute new coordinates for them
7845     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7846     TNodeXYZMap nBordXYZ;
7847     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7848     list< const SMDS_MeshNode* >::iterator nBordIt;
7849
7850     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7851     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7852     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7853     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7854     double tol2 = 1.e-8;
7855     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7856     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7857       // Need node movement.
7858
7859       // find X and Z axes to create trsf
7860       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7861       gp_Vec X = Zs ^ Zb;
7862       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7863         // Zb || Zs
7864         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7865
7866       // coord systems
7867       gp_Ax3 toBordAx( Pb1, Zb, X );
7868       gp_Ax3 fromSideAx( Ps1, Zs, X );
7869       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7870       // set trsf
7871       gp_Trsf toBordSys, fromSide2Sys;
7872       toBordSys.SetTransformation( toBordAx );
7873       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7874       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7875
7876       // move
7877       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7878         const SMDS_MeshNode* n = *nBordIt;
7879         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7880         toBordSys.Transforms( xyz );
7881         fromSide2Sys.Transforms( xyz );
7882         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7883       }
7884     }
7885     else {
7886       // just insert nodes XYZ in the nBordXYZ map
7887       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7888         const SMDS_MeshNode* n = *nBordIt;
7889         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7890       }
7891     }
7892
7893     // 2. On the side 2, find the links most co-directed with the correspondent
7894     //    links of the free border
7895
7896     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7897     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7898     sideNodes.push_back( theSideFirstNode );
7899
7900     bool hasVolumes = false;
7901     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7902     set<long> foundSideLinkIDs, checkedLinkIDs;
7903     SMDS_VolumeTool volume;
7904     //const SMDS_MeshNode* faceNodes[ 4 ];
7905
7906     const SMDS_MeshNode*    sideNode;
7907     const SMDS_MeshElement* sideElem  = 0;
7908     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7909     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7910     nBordIt = bordNodes.begin();
7911     nBordIt++;
7912     // border node position and border link direction to compare with
7913     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7914     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7915     // choose next side node by link direction or by closeness to
7916     // the current border node:
7917     bool searchByDir = ( *nBordIt != theBordLastNode );
7918     do {
7919       // find the next node on the Side 2
7920       sideNode = 0;
7921       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7922       long linkID;
7923       checkedLinkIDs.clear();
7924       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7925
7926       // loop on inverse elements of current node (prevSideNode) on the Side 2
7927       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7928       while ( invElemIt->more() )
7929       {
7930         const SMDS_MeshElement* elem = invElemIt->next();
7931         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7932         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7933         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7934         bool isVolume = volume.Set( elem );
7935         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7936         if ( isVolume ) // --volume
7937           hasVolumes = true;
7938         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7939           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7940           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7941           while ( nIt->more() ) {
7942             nodes[ iNode ] = cast2Node( nIt->next() );
7943             if ( nodes[ iNode++ ] == prevSideNode )
7944               iPrevNode = iNode - 1;
7945           }
7946           // there are 2 links to check
7947           nbNodes = 2;
7948         }
7949         else // --edge
7950           continue;
7951         // loop on links, to be precise, on the second node of links
7952         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7953           const SMDS_MeshNode* n = nodes[ iNode ];
7954           if ( isVolume ) {
7955             if ( !volume.IsLinked( n, prevSideNode ))
7956               continue;
7957           }
7958           else {
7959             if ( iNode ) // a node before prevSideNode
7960               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7961             else         // a node after prevSideNode
7962               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7963           }
7964           // check if this link was already used
7965           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7966           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7967           if (!isJustChecked &&
7968               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7969           {
7970             // test a link geometrically
7971             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7972             bool linkIsBetter = false;
7973             double dot = 0.0, dist = 0.0;
7974             if ( searchByDir ) { // choose most co-directed link
7975               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7976               linkIsBetter = ( dot > maxDot );
7977             }
7978             else { // choose link with the node closest to bordPos
7979               dist = ( nextXYZ - bordPos ).SquareModulus();
7980               linkIsBetter = ( dist < minDist );
7981             }
7982             if ( linkIsBetter ) {
7983               maxDot = dot;
7984               minDist = dist;
7985               linkID = iLink;
7986               sideNode = n;
7987               sideElem = elem;
7988             }
7989           }
7990         }
7991       } // loop on inverse elements of prevSideNode
7992
7993       if ( !sideNode ) {
7994         MESSAGE(" Can't find path by links of the Side 2 ");
7995         return SEW_BAD_SIDE_NODES;
7996       }
7997       sideNodes.push_back( sideNode );
7998       sideElems.push_back( sideElem );
7999       foundSideLinkIDs.insert ( linkID );
8000       prevSideNode = sideNode;
8001
8002       if ( *nBordIt == theBordLastNode )
8003         searchByDir = false;
8004       else {
8005         // find the next border link to compare with
8006         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8007         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8008         // move to next border node if sideNode is before forward border node (bordPos)
8009         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8010           prevBordNode = *nBordIt;
8011           nBordIt++;
8012           bordPos = nBordXYZ[ *nBordIt ];
8013           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8014           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8015         }
8016       }
8017     }
8018     while ( sideNode != theSideSecondNode );
8019
8020     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8021       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8022       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8023     }
8024   } // end nodes search on the side 2
8025
8026   // ============================
8027   // sew the border to the side 2
8028   // ============================
8029
8030   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8031   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8032
8033   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8034   if ( toMergeConformal && toCreatePolygons )
8035   {
8036     // do not merge quadrangles if polygons are OK (IPAL0052824)
8037     eIt[0] = eSide[0].begin();
8038     eIt[1] = eSide[1].begin();
8039     bool allQuads[2] = { true, true };
8040     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8041       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8042         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8043     }
8044     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8045   }
8046
8047   TListOfListOfNodes nodeGroupsToMerge;
8048   if (( toMergeConformal ) ||
8049       ( theSideIsFreeBorder && !theSideThirdNode )) {
8050
8051     // all nodes are to be merged
8052
8053     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8054          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8055          nIt[0]++, nIt[1]++ )
8056     {
8057       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8058       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8059       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8060     }
8061   }
8062   else {
8063
8064     // insert new nodes into the border and the side to get equal nb of segments
8065
8066     // get normalized parameters of nodes on the borders
8067     vector< double > param[ 2 ];
8068     param[0].resize( maxNbNodes );
8069     param[1].resize( maxNbNodes );
8070     int iNode, iBord;
8071     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8072       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8073       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8074       const SMDS_MeshNode* nPrev = *nIt;
8075       double bordLength = 0;
8076       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8077         const SMDS_MeshNode* nCur = *nIt;
8078         gp_XYZ segment (nCur->X() - nPrev->X(),
8079                         nCur->Y() - nPrev->Y(),
8080                         nCur->Z() - nPrev->Z());
8081         double segmentLen = segment.Modulus();
8082         bordLength += segmentLen;
8083         param[ iBord ][ iNode ] = bordLength;
8084         nPrev = nCur;
8085       }
8086       // normalize within [0,1]
8087       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8088         param[ iBord ][ iNode ] /= bordLength;
8089       }
8090     }
8091
8092     // loop on border segments
8093     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8094     int i[ 2 ] = { 0, 0 };
8095     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8096     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8097
8098     // element can be split while iterating on border if it has two edges in the border
8099     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8100     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8101
8102     TElemOfNodeListMap insertMap;
8103     TElemOfNodeListMap::iterator insertMapIt;
8104     // insertMap is
8105     // key:   elem to insert nodes into
8106     // value: 2 nodes to insert between + nodes to be inserted
8107     do {
8108       bool next[ 2 ] = { false, false };
8109
8110       // find min adjacent segment length after sewing
8111       double nextParam = 10., prevParam = 0;
8112       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8113         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8114           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8115         if ( i[ iBord ] > 0 )
8116           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8117       }
8118       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8119       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8120       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8121
8122       // choose to insert or to merge nodes
8123       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8124       if ( Abs( du ) <= minSegLen * 0.2 ) {
8125         // merge
8126         // ------
8127         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8128         const SMDS_MeshNode* n0 = *nIt[0];
8129         const SMDS_MeshNode* n1 = *nIt[1];
8130         nodeGroupsToMerge.back().push_back( n1 );
8131         nodeGroupsToMerge.back().push_back( n0 );
8132         // position of node of the border changes due to merge
8133         param[ 0 ][ i[0] ] += du;
8134         // move n1 for the sake of elem shape evaluation during insertion.
8135         // n1 will be removed by MergeNodes() anyway
8136         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8137         next[0] = next[1] = true;
8138       }
8139       else {
8140         // insert
8141         // ------
8142         int intoBord = ( du < 0 ) ? 0 : 1;
8143         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8144         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8145         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8146         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8147         if ( intoBord == 1 ) {
8148           // move node of the border to be on a link of elem of the side
8149           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8150           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8151           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8152           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8153         }
8154         elemReplaceMapIt = elemReplaceMap.find( elem );
8155         if ( elemReplaceMapIt != elemReplaceMap.end() )
8156           elem = elemReplaceMapIt->second;
8157
8158         insertMapIt = insertMap.find( elem );
8159         bool  notFound = ( insertMapIt == insertMap.end() );
8160         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8161         if ( otherLink ) {
8162           // insert into another link of the same element:
8163           // 1. perform insertion into the other link of the elem
8164           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8165           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8166           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8167           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8168           // 2. perform insertion into the link of adjacent faces
8169           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8170             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8171           }
8172           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8173             InsertNodesIntoLink( seg, n12, n22, nodeList );
8174           }
8175           if (toCreatePolyedrs) {
8176             // perform insertion into the links of adjacent volumes
8177             UpdateVolumes(n12, n22, nodeList);
8178           }
8179           // 3. find an element appeared on n1 and n2 after the insertion
8180           insertMap.erase( insertMapIt );
8181           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8182           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8183           elem = elem2;
8184         }
8185         if ( notFound || otherLink ) {
8186           // add element and nodes of the side into the insertMap
8187           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8188           (*insertMapIt).second.push_back( n1 );
8189           (*insertMapIt).second.push_back( n2 );
8190         }
8191         // add node to be inserted into elem
8192         (*insertMapIt).second.push_back( nIns );
8193         next[ 1 - intoBord ] = true;
8194       }
8195
8196       // go to the next segment
8197       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8198         if ( next[ iBord ] ) {
8199           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8200             eIt[ iBord ]++;
8201           nPrev[ iBord ] = *nIt[ iBord ];
8202           nIt[ iBord ]++; i[ iBord ]++;
8203         }
8204       }
8205     }
8206     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8207
8208     // perform insertion of nodes into elements
8209
8210     for (insertMapIt = insertMap.begin();
8211          insertMapIt != insertMap.end();
8212          insertMapIt++ )
8213     {
8214       const SMDS_MeshElement* elem = (*insertMapIt).first;
8215       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8216       if ( nodeList.size() < 3 ) continue;
8217       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8218       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8219
8220       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8221
8222       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8223         InsertNodesIntoLink( seg, n1, n2, nodeList );
8224       }
8225
8226       if ( !theSideIsFreeBorder ) {
8227         // look for and insert nodes into the faces adjacent to elem
8228         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8229           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8230         }
8231       }
8232       if (toCreatePolyedrs) {
8233         // perform insertion into the links of adjacent volumes
8234         UpdateVolumes(n1, n2, nodeList);
8235       }
8236     }
8237   } // end: insert new nodes
8238
8239   MergeNodes ( nodeGroupsToMerge );
8240
8241
8242   // Remove coincident segments
8243
8244   // get new segments
8245   TIDSortedElemSet segments;
8246   SMESH_SequenceOfElemPtr newFaces;
8247   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8248   {
8249     if ( !myLastCreatedElems[i] ) continue;
8250     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8251       segments.insert( segments.end(), myLastCreatedElems[i] );
8252     else
8253       newFaces.push_back( myLastCreatedElems[i] );
8254   }
8255   // get segments adjacent to merged nodes
8256   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8257   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8258   {
8259     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8260     if ( nodes.front()->IsNull() ) continue;
8261     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8262     while ( segIt->more() )
8263       segments.insert( segIt->next() );
8264   }
8265
8266   // find coincident
8267   TListOfListOfElementsID equalGroups;
8268   if ( !segments.empty() )
8269     FindEqualElements( segments, equalGroups );
8270   if ( !equalGroups.empty() )
8271   {
8272     // remove from segments those that will be removed
8273     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8274     for ( ; itGroups != equalGroups.end(); ++itGroups )
8275     {
8276       list< smIdType >& group = *itGroups;
8277       list< smIdType >::iterator id = group.begin();
8278       for ( ++id; id != group.end(); ++id )
8279         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8280           segments.erase( seg );
8281     }
8282     // remove equal segments
8283     MergeElements( equalGroups );
8284
8285     // restore myLastCreatedElems
8286     myLastCreatedElems = newFaces;
8287     TIDSortedElemSet::iterator seg = segments.begin();
8288     for ( ; seg != segments.end(); ++seg )
8289       myLastCreatedElems.push_back( *seg );
8290   }
8291
8292   return aResult;
8293 }
8294
8295 //=======================================================================
8296 //function : InsertNodesIntoLink
8297 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8298 //           and theBetweenNode2 and split theElement
8299 //=======================================================================
8300
8301 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8302                                            const SMDS_MeshNode*        theBetweenNode1,
8303                                            const SMDS_MeshNode*        theBetweenNode2,
8304                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8305                                            const bool                  toCreatePoly)
8306 {
8307   if ( !theElement ) return;
8308
8309   SMESHDS_Mesh *aMesh = GetMeshDS();
8310   vector<const SMDS_MeshElement*> newElems;
8311
8312   if ( theElement->GetType() == SMDSAbs_Edge )
8313   {
8314     theNodesToInsert.push_front( theBetweenNode1 );
8315     theNodesToInsert.push_back ( theBetweenNode2 );
8316     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8317     const SMDS_MeshNode* n1 = *n;
8318     for ( ++n; n != theNodesToInsert.end(); ++n )
8319     {
8320       const SMDS_MeshNode* n2 = *n;
8321       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8322         AddToSameGroups( seg, theElement, aMesh );
8323       else
8324         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8325       n1 = n2;
8326     }
8327     theNodesToInsert.pop_front();
8328     theNodesToInsert.pop_back();
8329
8330     if ( theElement->IsQuadratic() ) // add a not split part
8331     {
8332       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8333                                           theElement->end_nodes() );
8334       int iOther = 0, nbN = nodes.size();
8335       for ( ; iOther < nbN; ++iOther )
8336         if ( nodes[iOther] != theBetweenNode1 &&
8337              nodes[iOther] != theBetweenNode2 )
8338           break;
8339       if      ( iOther == 0 )
8340       {
8341         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8342           AddToSameGroups( seg, theElement, aMesh );
8343         else
8344           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8345       }
8346       else if ( iOther == 2 )
8347       {
8348         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8349           AddToSameGroups( seg, theElement, aMesh );
8350         else
8351           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8352       }
8353     }
8354     // treat new elements
8355     for ( size_t i = 0; i < newElems.size(); ++i )
8356       if ( newElems[i] )
8357       {
8358         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8359         myLastCreatedElems.push_back( newElems[i] );
8360       }
8361     ReplaceElemInGroups( theElement, newElems, aMesh );
8362     aMesh->RemoveElement( theElement );
8363     return;
8364
8365   } // if ( theElement->GetType() == SMDSAbs_Edge )
8366
8367   const SMDS_MeshElement* theFace = theElement;
8368   if ( theFace->GetType() != SMDSAbs_Face ) return;
8369
8370   // find indices of 2 link nodes and of the rest nodes
8371   int iNode = 0, il1, il2, i3, i4;
8372   il1 = il2 = i3 = i4 = -1;
8373   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8374
8375   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8376   while ( nodeIt->more() ) {
8377     const SMDS_MeshNode* n = nodeIt->next();
8378     if ( n == theBetweenNode1 )
8379       il1 = iNode;
8380     else if ( n == theBetweenNode2 )
8381       il2 = iNode;
8382     else if ( i3 < 0 )
8383       i3 = iNode;
8384     else
8385       i4 = iNode;
8386     nodes[ iNode++ ] = n;
8387   }
8388   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8389     return ;
8390
8391   // arrange link nodes to go one after another regarding the face orientation
8392   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8393   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8394   if ( reverse ) {
8395     iNode = il1;
8396     il1 = il2;
8397     il2 = iNode;
8398     aNodesToInsert.reverse();
8399   }
8400   // check that not link nodes of a quadrangles are in good order
8401   int nbFaceNodes = theFace->NbNodes();
8402   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8403     iNode = i3;
8404     i3 = i4;
8405     i4 = iNode;
8406   }
8407
8408   if (toCreatePoly || theFace->IsPoly()) {
8409
8410     iNode = 0;
8411     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8412
8413     // add nodes of face up to first node of link
8414     bool isFLN = false;
8415     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8416     while ( nodeIt->more() && !isFLN ) {
8417       const SMDS_MeshNode* n = nodeIt->next();
8418       poly_nodes[iNode++] = n;
8419       isFLN = ( n == nodes[il1] );
8420     }
8421     // add nodes to insert
8422     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8423     for (; nIt != aNodesToInsert.end(); nIt++) {
8424       poly_nodes[iNode++] = *nIt;
8425     }
8426     // add nodes of face starting from last node of link
8427     while ( nodeIt->more() ) {
8428       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8429       poly_nodes[iNode++] = n;
8430     }
8431
8432     // make a new face
8433     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8434   }
8435
8436   else if ( !theFace->IsQuadratic() )
8437   {
8438     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8439     int nbLinkNodes = 2 + aNodesToInsert.size();
8440     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8441     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8442     linkNodes[ 0 ] = nodes[ il1 ];
8443     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8444     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8445     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8446       linkNodes[ iNode++ ] = *nIt;
8447     }
8448     // decide how to split a quadrangle: compare possible variants
8449     // and choose which of splits to be a quadrangle
8450     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8451     if ( nbFaceNodes == 3 ) {
8452       iBestQuad = nbSplits;
8453       i4 = i3;
8454     }
8455     else if ( nbFaceNodes == 4 ) {
8456       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8457       double aBestRate = DBL_MAX;
8458       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8459         i1 = 0; i2 = 1;
8460         double aBadRate = 0;
8461         // evaluate elements quality
8462         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8463           if ( iSplit == iQuad ) {
8464             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8465                                    linkNodes[ i2++ ],
8466                                    nodes[ i3 ],
8467                                    nodes[ i4 ]);
8468             aBadRate += getBadRate( &quad, aCrit );
8469           }
8470           else {
8471             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8472                                    linkNodes[ i2++ ],
8473                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8474             aBadRate += getBadRate( &tria, aCrit );
8475           }
8476         }
8477         // choice
8478         if ( aBadRate < aBestRate ) {
8479           iBestQuad = iQuad;
8480           aBestRate = aBadRate;
8481         }
8482       }
8483     }
8484
8485     // create new elements
8486     i1 = 0; i2 = 1;
8487     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8488     {
8489       if ( iSplit == iBestQuad )
8490         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8491                                             linkNodes[ i2++ ],
8492                                             nodes[ i3 ],
8493                                             nodes[ i4 ]));
8494       else
8495         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8496                                             linkNodes[ i2++ ],
8497                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8498     }
8499
8500     const SMDS_MeshNode* newNodes[ 4 ];
8501     newNodes[ 0 ] = linkNodes[ i1 ];
8502     newNodes[ 1 ] = linkNodes[ i2 ];
8503     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8504     newNodes[ 3 ] = nodes[ i4 ];
8505     if (iSplit == iBestQuad)
8506       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8507     else
8508       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8509
8510   } // end if(!theFace->IsQuadratic())
8511
8512   else { // theFace is quadratic
8513     // we have to split theFace on simple triangles and one simple quadrangle
8514     int tmp = il1/2;
8515     int nbshift = tmp*2;
8516     // shift nodes in nodes[] by nbshift
8517     int i,j;
8518     for(i=0; i<nbshift; i++) {
8519       const SMDS_MeshNode* n = nodes[0];
8520       for(j=0; j<nbFaceNodes-1; j++) {
8521         nodes[j] = nodes[j+1];
8522       }
8523       nodes[nbFaceNodes-1] = n;
8524     }
8525     il1 = il1 - nbshift;
8526     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8527     //   n0      n1     n2    n0      n1     n2
8528     //     +-----+-----+        +-----+-----+
8529     //      \         /         |           |
8530     //       \       /          |           |
8531     //      n5+     +n3       n7+           +n3
8532     //         \   /            |           |
8533     //          \ /             |           |
8534     //           +              +-----+-----+
8535     //           n4           n6      n5     n4
8536
8537     // create new elements
8538     int n1,n2,n3;
8539     if ( nbFaceNodes == 6 ) { // quadratic triangle
8540       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8541       if ( theFace->IsMediumNode(nodes[il1]) ) {
8542         // create quadrangle
8543         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8544         n1 = 1;
8545         n2 = 2;
8546         n3 = 3;
8547       }
8548       else {
8549         // create quadrangle
8550         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8551         n1 = 0;
8552         n2 = 1;
8553         n3 = 5;
8554       }
8555     }
8556     else { // nbFaceNodes==8 - quadratic quadrangle
8557       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8558       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8559       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8560       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8561         // create quadrangle
8562         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8563         n1 = 1;
8564         n2 = 2;
8565         n3 = 3;
8566       }
8567       else {
8568         // create quadrangle
8569         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8570         n1 = 0;
8571         n2 = 1;
8572         n3 = 7;
8573       }
8574     }
8575     // create needed triangles using n1,n2,n3 and inserted nodes
8576     int nbn = 2 + aNodesToInsert.size();
8577     vector<const SMDS_MeshNode*> aNodes(nbn);
8578     aNodes[0    ] = nodes[n1];
8579     aNodes[nbn-1] = nodes[n2];
8580     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8581     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8582       aNodes[iNode++] = *nIt;
8583     }
8584     for ( i = 1; i < nbn; i++ )
8585       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8586   }
8587
8588   // remove the old face
8589   for ( size_t i = 0; i < newElems.size(); ++i )
8590     if ( newElems[i] )
8591     {
8592       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8593       myLastCreatedElems.push_back( newElems[i] );
8594     }
8595   ReplaceElemInGroups( theFace, newElems, aMesh );
8596   aMesh->RemoveElement(theFace);
8597
8598 } // InsertNodesIntoLink()
8599
8600 //=======================================================================
8601 //function : UpdateVolumes
8602 //purpose  :
8603 //=======================================================================
8604
8605 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8606                                       const SMDS_MeshNode*        theBetweenNode2,
8607                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8608 {
8609   ClearLastCreated();
8610
8611   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8612   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8613     const SMDS_MeshElement* elem = invElemIt->next();
8614
8615     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8616     SMDS_VolumeTool aVolume (elem);
8617     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8618       continue;
8619
8620     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8621     int iface, nbFaces = aVolume.NbFaces();
8622     vector<const SMDS_MeshNode *> poly_nodes;
8623     vector<int> quantities (nbFaces);
8624
8625     for (iface = 0; iface < nbFaces; iface++) {
8626       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8627       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8628       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8629
8630       for (int inode = 0; inode < nbFaceNodes; inode++) {
8631         poly_nodes.push_back(faceNodes[inode]);
8632
8633         if (nbInserted == 0) {
8634           if (faceNodes[inode] == theBetweenNode1) {
8635             if (faceNodes[inode + 1] == theBetweenNode2) {
8636               nbInserted = theNodesToInsert.size();
8637
8638               // add nodes to insert
8639               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8640               for (; nIt != theNodesToInsert.end(); nIt++) {
8641                 poly_nodes.push_back(*nIt);
8642               }
8643             }
8644           }
8645           else if (faceNodes[inode] == theBetweenNode2) {
8646             if (faceNodes[inode + 1] == theBetweenNode1) {
8647               nbInserted = theNodesToInsert.size();
8648
8649               // add nodes to insert in reversed order
8650               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8651               nIt--;
8652               for (; nIt != theNodesToInsert.begin(); nIt--) {
8653                 poly_nodes.push_back(*nIt);
8654               }
8655               poly_nodes.push_back(*nIt);
8656             }
8657           }
8658           else {
8659           }
8660         }
8661       }
8662       quantities[iface] = nbFaceNodes + nbInserted;
8663     }
8664
8665     // Replace the volume
8666     SMESHDS_Mesh *aMesh = GetMeshDS();
8667
8668     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8669     {
8670       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8671       myLastCreatedElems.push_back( newElem );
8672       ReplaceElemInGroups( elem, newElem, aMesh );
8673     }
8674     aMesh->RemoveElement( elem );
8675   }
8676 }
8677
8678 namespace
8679 {
8680   //================================================================================
8681   /*!
8682    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8683    */
8684   //================================================================================
8685
8686   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8687                            vector<const SMDS_MeshNode *> & nodes,
8688                            vector<int> &                   nbNodeInFaces )
8689   {
8690     nodes.clear();
8691     nbNodeInFaces.clear();
8692     SMDS_VolumeTool vTool ( elem );
8693     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8694     {
8695       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8696       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8697       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8698     }
8699   }
8700 }
8701
8702 //=======================================================================
8703 /*!
8704  * \brief Convert elements contained in a sub-mesh to quadratic
8705  * \return int - nb of checked elements
8706  */
8707 //=======================================================================
8708
8709 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8710                                                   SMESH_MesherHelper& theHelper,
8711                                                   const bool          theForce3d)
8712 {
8713   //MESSAGE("convertElemToQuadratic");
8714   smIdType nbElem = 0;
8715   if( !theSm ) return nbElem;
8716
8717   vector<int> nbNodeInFaces;
8718   vector<const SMDS_MeshNode *> nodes;
8719   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8720   while(ElemItr->more())
8721   {
8722     nbElem++;
8723     const SMDS_MeshElement* elem = ElemItr->next();
8724     if( !elem ) continue;
8725
8726     // analyse a necessity of conversion
8727     const SMDSAbs_ElementType aType = elem->GetType();
8728     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8729       continue;
8730     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8731     bool hasCentralNodes = false;
8732     if ( elem->IsQuadratic() )
8733     {
8734       bool alreadyOK;
8735       switch ( aGeomType ) {
8736       case SMDSEntity_Quad_Triangle:
8737       case SMDSEntity_Quad_Quadrangle:
8738       case SMDSEntity_Quad_Hexa:
8739       case SMDSEntity_Quad_Penta:
8740         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8741
8742       case SMDSEntity_BiQuad_Triangle:
8743       case SMDSEntity_BiQuad_Quadrangle:
8744       case SMDSEntity_TriQuad_Hexa:
8745       case SMDSEntity_BiQuad_Penta:
8746         alreadyOK = theHelper.GetIsBiQuadratic();
8747         hasCentralNodes = true;
8748         break;
8749       default:
8750         alreadyOK = true;
8751       }
8752       // take into account already present medium nodes
8753       switch ( aType ) {
8754       case SMDSAbs_Volume:
8755         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8756       case SMDSAbs_Face:
8757         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8758       case SMDSAbs_Edge:
8759         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8760       default:;
8761       }
8762       if ( alreadyOK )
8763         continue;
8764     }
8765     // get elem data needed to re-create it
8766     //
8767     const smIdType id = elem->GetID();
8768     const int nbNodes = elem->NbCornerNodes();
8769     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8770     if ( aGeomType == SMDSEntity_Polyhedra )
8771       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8772     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8773       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8774
8775     // remove a linear element
8776     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8777
8778     // remove central nodes of biquadratic elements (biquad->quad conversion)
8779     if ( hasCentralNodes )
8780       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8781         if ( nodes[i]->NbInverseElements() == 0 )
8782           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8783
8784     const SMDS_MeshElement* NewElem = 0;
8785
8786     switch( aType )
8787     {
8788     case SMDSAbs_Edge :
8789     {
8790       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8791       break;
8792     }
8793     case SMDSAbs_Face :
8794     {
8795       switch(nbNodes)
8796       {
8797       case 3:
8798         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8799         break;
8800       case 4:
8801         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8802         break;
8803       default:
8804         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8805       }
8806       break;
8807     }
8808     case SMDSAbs_Volume :
8809     {
8810       switch( aGeomType )
8811       {
8812       case SMDSEntity_Tetra:
8813         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8814         break;
8815       case SMDSEntity_Pyramid:
8816         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8817         break;
8818       case SMDSEntity_Penta:
8819       case SMDSEntity_Quad_Penta:
8820       case SMDSEntity_BiQuad_Penta:
8821         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8822         break;
8823       case SMDSEntity_Hexa:
8824       case SMDSEntity_Quad_Hexa:
8825       case SMDSEntity_TriQuad_Hexa:
8826         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8827                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8828         break;
8829       case SMDSEntity_Hexagonal_Prism:
8830       default:
8831         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8832       }
8833       break;
8834     }
8835     default :
8836       continue;
8837     }
8838     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8839     if( NewElem && NewElem->getshapeId() < 1 )
8840       theSm->AddElement( NewElem );
8841   }
8842   return nbElem;
8843 }
8844 //=======================================================================
8845 //function : ConvertToQuadratic
8846 //purpose  :
8847 //=======================================================================
8848
8849 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8850 {
8851   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8852   SMESHDS_Mesh* meshDS = GetMeshDS();
8853
8854   SMESH_MesherHelper aHelper(*myMesh);
8855
8856   aHelper.SetIsQuadratic( true );
8857   aHelper.SetIsBiQuadratic( theToBiQuad );
8858   aHelper.SetElementsOnShape(true);
8859   aHelper.ToFixNodeParameters( true );
8860
8861   // convert elements assigned to sub-meshes
8862   smIdType nbCheckedElems = 0;
8863   if ( myMesh->HasShapeToMesh() )
8864   {
8865     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8866     {
8867       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8868       while ( smIt->more() ) {
8869         SMESH_subMesh* sm = smIt->next();
8870         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8871           aHelper.SetSubShape( sm->GetSubShape() );
8872           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8873         }
8874       }
8875     }
8876   }
8877
8878   // convert elements NOT assigned to sub-meshes
8879   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8880   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8881   {
8882     aHelper.SetElementsOnShape(false);
8883     SMESHDS_SubMesh *smDS = 0;
8884
8885     // convert edges
8886     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8887     while( aEdgeItr->more() )
8888     {
8889       const SMDS_MeshEdge* edge = aEdgeItr->next();
8890       if ( !edge->IsQuadratic() )
8891       {
8892         smIdType                  id = edge->GetID();
8893         const SMDS_MeshNode* n1 = edge->GetNode(0);
8894         const SMDS_MeshNode* n2 = edge->GetNode(1);
8895
8896         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8897
8898         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8899         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8900       }
8901       else
8902       {
8903         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8904       }
8905     }
8906
8907     // convert faces
8908     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8909     while( aFaceItr->more() )
8910     {
8911       const SMDS_MeshFace* face = aFaceItr->next();
8912       if ( !face ) continue;
8913       
8914       const SMDSAbs_EntityType type = face->GetEntityType();
8915       bool alreadyOK;
8916       switch( type )
8917       {
8918       case SMDSEntity_Quad_Triangle:
8919       case SMDSEntity_Quad_Quadrangle:
8920         alreadyOK = !theToBiQuad;
8921         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8922         break;
8923       case SMDSEntity_BiQuad_Triangle:
8924       case SMDSEntity_BiQuad_Quadrangle:
8925         alreadyOK = theToBiQuad;
8926         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8927         break;
8928       default: alreadyOK = false;
8929       }
8930       if ( alreadyOK )
8931         continue;
8932
8933       const smIdType id = face->GetID();
8934       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8935
8936       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8937
8938       SMDS_MeshFace * NewFace = 0;
8939       switch( type )
8940       {
8941       case SMDSEntity_Triangle:
8942       case SMDSEntity_Quad_Triangle:
8943       case SMDSEntity_BiQuad_Triangle:
8944         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8945         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8946           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8947         break;
8948
8949       case SMDSEntity_Quadrangle:
8950       case SMDSEntity_Quad_Quadrangle:
8951       case SMDSEntity_BiQuad_Quadrangle:
8952         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8953         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8954           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8955         break;
8956
8957       default:;
8958         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8959       }
8960       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8961     }
8962
8963     // convert volumes
8964     vector<int> nbNodeInFaces;
8965     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8966     while(aVolumeItr->more())
8967     {
8968       const SMDS_MeshVolume* volume = aVolumeItr->next();
8969       if ( !volume ) continue;
8970
8971       const SMDSAbs_EntityType type = volume->GetEntityType();
8972       if ( volume->IsQuadratic() )
8973       {
8974         bool alreadyOK;
8975         switch ( type )
8976         {
8977         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8978         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8979         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8980         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8981         default:                      alreadyOK = true;
8982         }
8983         if ( alreadyOK )
8984         {
8985           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8986           continue;
8987         }
8988       }
8989       const smIdType id = volume->GetID();
8990       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8991       if ( type == SMDSEntity_Polyhedra )
8992         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8993       else if ( type == SMDSEntity_Hexagonal_Prism )
8994         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8995
8996       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8997
8998       SMDS_MeshVolume * NewVolume = 0;
8999       switch ( type )
9000       {
9001       case SMDSEntity_Tetra:
9002         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9003         break;
9004       case SMDSEntity_Hexa:
9005       case SMDSEntity_Quad_Hexa:
9006       case SMDSEntity_TriQuad_Hexa:
9007         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9008                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9009         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9010           if ( nodes[i]->NbInverseElements() == 0 )
9011             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9012         break;
9013       case SMDSEntity_Pyramid:
9014         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9015                                       nodes[3], nodes[4], id, theForce3d);
9016         break;
9017       case SMDSEntity_Penta:
9018       case SMDSEntity_Quad_Penta:
9019       case SMDSEntity_BiQuad_Penta:
9020         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9021                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9022         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9023           if ( nodes[i]->NbInverseElements() == 0 )
9024             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9025         break;
9026       case SMDSEntity_Hexagonal_Prism:
9027       default:
9028         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9029       }
9030       ReplaceElemInGroups(volume, NewVolume, meshDS);
9031     }
9032   }
9033
9034   if ( !theForce3d )
9035   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9036     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9037     // aHelper.FixQuadraticElements(myError);
9038     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9039   }
9040 }
9041
9042 //================================================================================
9043 /*!
9044  * \brief Makes given elements quadratic
9045  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9046  *  \param theElements - elements to make quadratic
9047  */
9048 //================================================================================
9049
9050 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9051                                           TIDSortedElemSet& theElements,
9052                                           const bool        theToBiQuad)
9053 {
9054   if ( theElements.empty() ) return;
9055
9056   // we believe that all theElements are of the same type
9057   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9058
9059   // get all nodes shared by theElements
9060   TIDSortedNodeSet allNodes;
9061   TIDSortedElemSet::iterator eIt = theElements.begin();
9062   for ( ; eIt != theElements.end(); ++eIt )
9063     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9064
9065   // complete theElements with elements of lower dim whose all nodes are in allNodes
9066
9067   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9068   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9069   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9070   for ( ; nIt != allNodes.end(); ++nIt )
9071   {
9072     const SMDS_MeshNode* n = *nIt;
9073     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9074     while ( invIt->more() )
9075     {
9076       const SMDS_MeshElement*      e = invIt->next();
9077       const SMDSAbs_ElementType type = e->GetType();
9078       if ( e->IsQuadratic() )
9079       {
9080         quadAdjacentElems[ type ].insert( e );
9081
9082         bool alreadyOK;
9083         switch ( e->GetEntityType() ) {
9084         case SMDSEntity_Quad_Triangle:
9085         case SMDSEntity_Quad_Quadrangle:
9086         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9087         case SMDSEntity_BiQuad_Triangle:
9088         case SMDSEntity_BiQuad_Quadrangle:
9089         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9090         default:                           alreadyOK = true;
9091         }
9092         if ( alreadyOK )
9093           continue;
9094       }
9095       if ( type >= elemType )
9096         continue; // same type or more complex linear element
9097
9098       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9099         continue; // e is already checked
9100
9101       // check nodes
9102       bool allIn = true;
9103       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9104       while ( nodeIt->more() && allIn )
9105         allIn = allNodes.count( nodeIt->next() );
9106       if ( allIn )
9107         theElements.insert(e );
9108     }
9109   }
9110
9111   SMESH_MesherHelper helper(*myMesh);
9112   helper.SetIsQuadratic( true );
9113   helper.SetIsBiQuadratic( theToBiQuad );
9114
9115   // add links of quadratic adjacent elements to the helper
9116
9117   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9118     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9119           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9120     {
9121       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9122     }
9123   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9124     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9125           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9126     {
9127       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9128     }
9129   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9130     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9131           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9132     {
9133       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9134     }
9135
9136   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9137
9138   SMESHDS_Mesh*  meshDS = GetMeshDS();
9139   SMESHDS_SubMesh* smDS = 0;
9140   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9141   {
9142     const SMDS_MeshElement* elem = *eIt;
9143
9144     bool alreadyOK;
9145     int nbCentralNodes = 0;
9146     switch ( elem->GetEntityType() ) {
9147       // linear convertible
9148     case SMDSEntity_Edge:
9149     case SMDSEntity_Triangle:
9150     case SMDSEntity_Quadrangle:
9151     case SMDSEntity_Tetra:
9152     case SMDSEntity_Pyramid:
9153     case SMDSEntity_Hexa:
9154     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9155       // quadratic that can become bi-quadratic
9156     case SMDSEntity_Quad_Triangle:
9157     case SMDSEntity_Quad_Quadrangle:
9158     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9159       // bi-quadratic
9160     case SMDSEntity_BiQuad_Triangle:
9161     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9162     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9163       // the rest
9164     default:                           alreadyOK = true;
9165     }
9166     if ( alreadyOK ) continue;
9167
9168     const SMDSAbs_ElementType type = elem->GetType();
9169     const smIdType              id = elem->GetID();
9170     const int              nbNodes = elem->NbCornerNodes();
9171     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9172
9173     helper.SetSubShape( elem->getshapeId() );
9174
9175     if ( !smDS || !smDS->Contains( elem ))
9176       smDS = meshDS->MeshElements( elem->getshapeId() );
9177     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9178
9179     SMDS_MeshElement * newElem = 0;
9180     switch( nbNodes )
9181     {
9182     case 4: // cases for most frequently used element types go first (for optimization)
9183       if ( type == SMDSAbs_Volume )
9184         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9185       else
9186         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9187       break;
9188     case 8:
9189       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9190                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9191       break;
9192     case 3:
9193       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9194       break;
9195     case 2:
9196       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9197       break;
9198     case 5:
9199       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9200                                  nodes[4], id, theForce3d);
9201       break;
9202     case 6:
9203       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9204                                  nodes[4], nodes[5], id, theForce3d);
9205       break;
9206     default:;
9207     }
9208     ReplaceElemInGroups( elem, newElem, meshDS);
9209     if( newElem && smDS )
9210       smDS->AddElement( newElem );
9211
9212     // remove central nodes
9213     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9214       if ( nodes[i]->NbInverseElements() == 0 )
9215         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9216
9217   } // loop on theElements
9218
9219   if ( !theForce3d )
9220   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9221     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9222     // helper.FixQuadraticElements( myError );
9223     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9224   }
9225 }
9226
9227 //=======================================================================
9228 /*!
9229  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9230  * \return smIdType - nb of checked elements
9231  */
9232 //=======================================================================
9233
9234 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9235                                           SMDS_ElemIteratorPtr theItr,
9236                                           const int            /*theShapeID*/)
9237 {
9238   smIdType nbElem = 0;
9239   SMESHDS_Mesh* meshDS = GetMeshDS();
9240   ElemFeatures elemType;
9241   vector<const SMDS_MeshNode *> nodes;
9242
9243   while( theItr->more() )
9244   {
9245     const SMDS_MeshElement* elem = theItr->next();
9246     nbElem++;
9247     if( elem && elem->IsQuadratic())
9248     {
9249       // get elem data
9250       int nbCornerNodes = elem->NbCornerNodes();
9251       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9252
9253       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9254
9255       //remove a quadratic element
9256       if ( !theSm || !theSm->Contains( elem ))
9257         theSm = meshDS->MeshElements( elem->getshapeId() );
9258       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9259
9260       // remove medium nodes
9261       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9262         if ( nodes[i]->NbInverseElements() == 0 )
9263           meshDS->RemoveFreeNode( nodes[i], theSm );
9264
9265       // add a linear element
9266       nodes.resize( nbCornerNodes );
9267       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9268       ReplaceElemInGroups(elem, newElem, meshDS);
9269       if( theSm && newElem )
9270         theSm->AddElement( newElem );
9271     }
9272   }
9273   return nbElem;
9274 }
9275
9276 //=======================================================================
9277 //function : ConvertFromQuadratic
9278 //purpose  :
9279 //=======================================================================
9280
9281 bool SMESH_MeshEditor::ConvertFromQuadratic()
9282 {
9283   smIdType nbCheckedElems = 0;
9284   if ( myMesh->HasShapeToMesh() )
9285   {
9286     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9287     {
9288       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9289       while ( smIt->more() ) {
9290         SMESH_subMesh* sm = smIt->next();
9291         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9292           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9293       }
9294     }
9295   }
9296
9297   smIdType totalNbElems =
9298     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9299   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9300   {
9301     SMESHDS_SubMesh *aSM = 0;
9302     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9303   }
9304
9305   return true;
9306 }
9307
9308 namespace
9309 {
9310   //================================================================================
9311   /*!
9312    * \brief Return true if all medium nodes of the element are in the node set
9313    */
9314   //================================================================================
9315
9316   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9317   {
9318     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9319       if ( !nodeSet.count( elem->GetNode(i) ))
9320         return false;
9321     return true;
9322   }
9323 }
9324
9325 //================================================================================
9326 /*!
9327  * \brief Makes given elements linear
9328  */
9329 //================================================================================
9330
9331 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9332 {
9333   if ( theElements.empty() ) return;
9334
9335   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9336   set<smIdType> mediumNodeIDs;
9337   TIDSortedElemSet::iterator eIt = theElements.begin();
9338   for ( ; eIt != theElements.end(); ++eIt )
9339   {
9340     const SMDS_MeshElement* e = *eIt;
9341     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9342       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9343   }
9344
9345   // replace given elements by linear ones
9346   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9347   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9348
9349   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9350   // except those elements sharing medium nodes of quadratic element whose medium nodes
9351   // are not all in mediumNodeIDs
9352
9353   // get remaining medium nodes
9354   TIDSortedNodeSet mediumNodes;
9355   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9356   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9357     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9358       mediumNodes.insert( mediumNodes.end(), n );
9359
9360   // find more quadratic elements to convert
9361   TIDSortedElemSet moreElemsToConvert;
9362   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9363   for ( ; nIt != mediumNodes.end(); ++nIt )
9364   {
9365     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9366     while ( invIt->more() )
9367     {
9368       const SMDS_MeshElement* e = invIt->next();
9369       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9370       {
9371         // find a more complex element including e and
9372         // whose medium nodes are not in mediumNodes
9373         bool complexFound = false;
9374         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9375         {
9376           SMDS_ElemIteratorPtr invIt2 =
9377             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9378           while ( invIt2->more() )
9379           {
9380             const SMDS_MeshElement* eComplex = invIt2->next();
9381             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9382             {
9383               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9384               if ( nbCommonNodes == e->NbNodes())
9385               {
9386                 complexFound = true;
9387                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9388                 break;
9389               }
9390             }
9391           }
9392         }
9393         if ( !complexFound )
9394           moreElemsToConvert.insert( e );
9395       }
9396     }
9397   }
9398   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9399   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9400 }
9401
9402 //=======================================================================
9403 //function : SewSideElements
9404 //purpose  :
9405 //=======================================================================
9406
9407 SMESH_MeshEditor::Sew_Error
9408 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9409                                    TIDSortedElemSet&    theSide2,
9410                                    const SMDS_MeshNode* theFirstNode1,
9411                                    const SMDS_MeshNode* theFirstNode2,
9412                                    const SMDS_MeshNode* theSecondNode1,
9413                                    const SMDS_MeshNode* theSecondNode2)
9414 {
9415   ClearLastCreated();
9416
9417   if ( theSide1.size() != theSide2.size() )
9418     return SEW_DIFF_NB_OF_ELEMENTS;
9419
9420   Sew_Error aResult = SEW_OK;
9421   // Algo:
9422   // 1. Build set of faces representing each side
9423   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9424   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9425
9426   // =======================================================================
9427   // 1. Build set of faces representing each side:
9428   // =======================================================================
9429   // a. build set of nodes belonging to faces
9430   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9431   // c. create temporary faces representing side of volumes if correspondent
9432   //    face does not exist
9433
9434   SMESHDS_Mesh* aMesh = GetMeshDS();
9435   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9436   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9437   TIDSortedElemSet             faceSet1, faceSet2;
9438   set<const SMDS_MeshElement*> volSet1,  volSet2;
9439   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9440   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9441   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9442   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9443   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9444   int iSide, iFace, iNode;
9445
9446   list<const SMDS_MeshElement* > tempFaceList;
9447   for ( iSide = 0; iSide < 2; iSide++ ) {
9448     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9449     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9450     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9451     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9452     set<const SMDS_MeshElement*>::iterator vIt;
9453     TIDSortedElemSet::iterator eIt;
9454     set<const SMDS_MeshNode*>::iterator    nIt;
9455
9456     // check that given nodes belong to given elements
9457     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9458     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9459     int firstIndex = -1, secondIndex = -1;
9460     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9461       const SMDS_MeshElement* elem = *eIt;
9462       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9463       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9464       if ( firstIndex > -1 && secondIndex > -1 ) break;
9465     }
9466     if ( firstIndex < 0 || secondIndex < 0 ) {
9467       // we can simply return until temporary faces created
9468       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9469     }
9470
9471     // -----------------------------------------------------------
9472     // 1a. Collect nodes of existing faces
9473     //     and build set of face nodes in order to detect missing
9474     //     faces corresponding to sides of volumes
9475     // -----------------------------------------------------------
9476
9477     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9478
9479     // loop on the given element of a side
9480     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9481       //const SMDS_MeshElement* elem = *eIt;
9482       const SMDS_MeshElement* elem = *eIt;
9483       if ( elem->GetType() == SMDSAbs_Face ) {
9484         faceSet->insert( elem );
9485         set <const SMDS_MeshNode*> faceNodeSet;
9486         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9487         while ( nodeIt->more() ) {
9488           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9489           nodeSet->insert( n );
9490           faceNodeSet.insert( n );
9491         }
9492         setOfFaceNodeSet.insert( faceNodeSet );
9493       }
9494       else if ( elem->GetType() == SMDSAbs_Volume )
9495         volSet->insert( elem );
9496     }
9497     // ------------------------------------------------------------------------------
9498     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9499     // ------------------------------------------------------------------------------
9500
9501     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9502       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9503       while ( fIt->more() ) { // loop on faces sharing a node
9504         const SMDS_MeshElement* f = fIt->next();
9505         if ( faceSet->find( f ) == faceSet->end() ) {
9506           // check if all nodes are in nodeSet and
9507           // complete setOfFaceNodeSet if they are
9508           set <const SMDS_MeshNode*> faceNodeSet;
9509           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9510           bool allInSet = true;
9511           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9512             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9513             if ( nodeSet->find( n ) == nodeSet->end() )
9514               allInSet = false;
9515             else
9516               faceNodeSet.insert( n );
9517           }
9518           if ( allInSet ) {
9519             faceSet->insert( f );
9520             setOfFaceNodeSet.insert( faceNodeSet );
9521           }
9522         }
9523       }
9524     }
9525
9526     // -------------------------------------------------------------------------
9527     // 1c. Create temporary faces representing sides of volumes if correspondent
9528     //     face does not exist
9529     // -------------------------------------------------------------------------
9530
9531     if ( !volSet->empty() ) {
9532       //int nodeSetSize = nodeSet->size();
9533
9534       // loop on given volumes
9535       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9536         SMDS_VolumeTool vol (*vIt);
9537         // loop on volume faces: find free faces
9538         // --------------------------------------
9539         list<const SMDS_MeshElement* > freeFaceList;
9540         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9541           if ( !vol.IsFreeFace( iFace ))
9542             continue;
9543           // check if there is already a face with same nodes in a face set
9544           const SMDS_MeshElement* aFreeFace = 0;
9545           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9546           int nbNodes = vol.NbFaceNodes( iFace );
9547           set <const SMDS_MeshNode*> faceNodeSet;
9548           vol.GetFaceNodes( iFace, faceNodeSet );
9549           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9550           if ( isNewFace ) {
9551             // no such a face is given but it still can exist, check it
9552             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9553             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9554           }
9555           if ( !aFreeFace ) {
9556             // create a temporary face
9557             if ( nbNodes == 3 ) {
9558               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9559               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9560             }
9561             else if ( nbNodes == 4 ) {
9562               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9563               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9564             }
9565             else {
9566               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9567               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9568               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9569             }
9570             if ( aFreeFace )
9571               tempFaceList.push_back( aFreeFace );
9572           }
9573
9574           if ( aFreeFace )
9575             freeFaceList.push_back( aFreeFace );
9576
9577         } // loop on faces of a volume
9578
9579         // choose one of several free faces of a volume
9580         // --------------------------------------------
9581         if ( freeFaceList.size() > 1 ) {
9582           // choose a face having max nb of nodes shared by other elems of a side
9583           int maxNbNodes = -1;
9584           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9585           while ( fIt != freeFaceList.end() ) { // loop on free faces
9586             int nbSharedNodes = 0;
9587             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9588             while ( nodeIt->more() ) { // loop on free face nodes
9589               const SMDS_MeshNode* n =
9590                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9592               while ( invElemIt->more() ) {
9593                 const SMDS_MeshElement* e = invElemIt->next();
9594                 nbSharedNodes += faceSet->count( e );
9595                 nbSharedNodes += elemSet->count( e );
9596               }
9597             }
9598             if ( nbSharedNodes > maxNbNodes ) {
9599               maxNbNodes = nbSharedNodes;
9600               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9601             }
9602             else if ( nbSharedNodes == maxNbNodes ) {
9603               fIt++;
9604             }
9605             else {
9606               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9607             }
9608           }
9609           if ( freeFaceList.size() > 1 )
9610           {
9611             // could not choose one face, use another way
9612             // choose a face most close to the bary center of the opposite side
9613             gp_XYZ aBC( 0., 0., 0. );
9614             set <const SMDS_MeshNode*> addedNodes;
9615             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9616             eIt = elemSet2->begin();
9617             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9618               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9619               while ( nodeIt->more() ) { // loop on free face nodes
9620                 const SMDS_MeshNode* n =
9621                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9622                 if ( addedNodes.insert( n ).second )
9623                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9624               }
9625             }
9626             aBC /= addedNodes.size();
9627             double minDist = DBL_MAX;
9628             fIt = freeFaceList.begin();
9629             while ( fIt != freeFaceList.end() ) { // loop on free faces
9630               double dist = 0;
9631               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9632               while ( nodeIt->more() ) { // loop on free face nodes
9633                 const SMDS_MeshNode* n =
9634                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9635                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9636                 dist += ( aBC - p ).SquareModulus();
9637               }
9638               if ( dist < minDist ) {
9639                 minDist = dist;
9640                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9641               }
9642               else
9643                 fIt = freeFaceList.erase( fIt++ );
9644             }
9645           }
9646         } // choose one of several free faces of a volume
9647
9648         if ( freeFaceList.size() == 1 ) {
9649           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9650           faceSet->insert( aFreeFace );
9651           // complete a node set with nodes of a found free face
9652           //           for ( iNode = 0; iNode < ; iNode++ )
9653           //             nodeSet->insert( fNodes[ iNode ] );
9654         }
9655
9656       } // loop on volumes of a side
9657
9658       //       // complete a set of faces if new nodes in a nodeSet appeared
9659       //       // ----------------------------------------------------------
9660       //       if ( nodeSetSize != nodeSet->size() ) {
9661       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9662       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9663       //           while ( fIt->more() ) { // loop on faces sharing a node
9664       //             const SMDS_MeshElement* f = fIt->next();
9665       //             if ( faceSet->find( f ) == faceSet->end() ) {
9666       //               // check if all nodes are in nodeSet and
9667       //               // complete setOfFaceNodeSet if they are
9668       //               set <const SMDS_MeshNode*> faceNodeSet;
9669       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9670       //               bool allInSet = true;
9671       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9672       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9673       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9674       //                   allInSet = false;
9675       //                 else
9676       //                   faceNodeSet.insert( n );
9677       //               }
9678       //               if ( allInSet ) {
9679       //                 faceSet->insert( f );
9680       //                 setOfFaceNodeSet.insert( faceNodeSet );
9681       //               }
9682       //             }
9683       //           }
9684       //         }
9685       //       }
9686     } // Create temporary faces, if there are volumes given
9687   } // loop on sides
9688
9689   if ( faceSet1.size() != faceSet2.size() ) {
9690     // delete temporary faces: they are in reverseElements of actual nodes
9691     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9692     //    while ( tmpFaceIt->more() )
9693     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9694     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9695     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9696     //      aMesh->RemoveElement(*tmpFaceIt);
9697     MESSAGE("Diff nb of faces");
9698     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9699   }
9700
9701   // ============================================================
9702   // 2. Find nodes to merge:
9703   //              bind a node to remove to a node to put instead
9704   // ============================================================
9705
9706   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9707   if ( theFirstNode1 != theFirstNode2 )
9708     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9709   if ( theSecondNode1 != theSecondNode2 )
9710     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9711
9712   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9713   set< long > linkIdSet; // links to process
9714   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9715
9716   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9717   list< NLink > linkList[2];
9718   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9719   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9720   // loop on links in linkList; find faces by links and append links
9721   // of the found faces to linkList
9722   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9723   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9724   {
9725     NLink link[] = { *linkIt[0], *linkIt[1] };
9726     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9727     if ( !linkIdSet.count( linkID ) )
9728       continue;
9729
9730     // by links, find faces in the face sets,
9731     // and find indices of link nodes in the found faces;
9732     // in a face set, there is only one or no face sharing a link
9733     // ---------------------------------------------------------------
9734
9735     const SMDS_MeshElement* face[] = { 0, 0 };
9736     vector<const SMDS_MeshNode*> fnodes[2];
9737     int iLinkNode[2][2];
9738     TIDSortedElemSet avoidSet;
9739     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9740       const SMDS_MeshNode* n1 = link[iSide].first;
9741       const SMDS_MeshNode* n2 = link[iSide].second;
9742       //cout << "Side " << iSide << " ";
9743       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9744       // find a face by two link nodes
9745       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9746                                                       *faceSetPtr[ iSide ], avoidSet,
9747                                                       &iLinkNode[iSide][0],
9748                                                       &iLinkNode[iSide][1] );
9749       if ( face[ iSide ])
9750       {
9751         //cout << " F " << face[ iSide]->GetID() <<endl;
9752         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9753         // put face nodes to fnodes
9754         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9755         fnodes[ iSide ].assign( nIt, nEnd );
9756         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9757       }
9758     }
9759
9760     // check similarity of elements of the sides
9761     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9762       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9763       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9764         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9765       }
9766       else {
9767         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9768       }
9769       break; // do not return because it's necessary to remove tmp faces
9770     }
9771
9772     // set nodes to merge
9773     // -------------------
9774
9775     if ( face[0] && face[1] )  {
9776       const int nbNodes = face[0]->NbNodes();
9777       if ( nbNodes != face[1]->NbNodes() ) {
9778         MESSAGE("Diff nb of face nodes");
9779         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9780         break; // do not return because it s necessary to remove tmp faces
9781       }
9782       bool reverse[] = { false, false }; // order of nodes in the link
9783       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9784         // analyse link orientation in faces
9785         int i1 = iLinkNode[ iSide ][ 0 ];
9786         int i2 = iLinkNode[ iSide ][ 1 ];
9787         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9788       }
9789       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9790       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9791       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9792       {
9793         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9794                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9795       }
9796
9797       // add other links of the faces to linkList
9798       // -----------------------------------------
9799
9800       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9801         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9802         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9803         if ( !iter_isnew.second ) { // already in a set: no need to process
9804           linkIdSet.erase( iter_isnew.first );
9805         }
9806         else // new in set == encountered for the first time: add
9807         {
9808           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9809           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9810           linkList[0].push_back ( NLink( n1, n2 ));
9811           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9812         }
9813       }
9814     } // 2 faces found
9815
9816     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9817       break;
9818
9819   } // loop on link lists
9820
9821   if ( aResult == SEW_OK &&
9822        ( //linkIt[0] != linkList[0].end() ||
9823         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9824     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9825              " " << (faceSetPtr[1]->empty()));
9826     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9827   }
9828
9829   // ====================================================================
9830   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9831   // ====================================================================
9832
9833   // delete temporary faces
9834   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9835   //  while ( tmpFaceIt->more() )
9836   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9837   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9838   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9839     aMesh->RemoveElement(*tmpFaceIt);
9840
9841   if ( aResult != SEW_OK)
9842     return aResult;
9843
9844   list< smIdType > nodeIDsToRemove;
9845   vector< const SMDS_MeshNode*> nodes;
9846   ElemFeatures elemType;
9847
9848   // loop on nodes replacement map
9849   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9850   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9851     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9852     {
9853       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9854       nodeIDsToRemove.push_back( nToRemove->GetID() );
9855       // loop on elements sharing nToRemove
9856       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9857       while ( invElemIt->more() ) {
9858         const SMDS_MeshElement* e = invElemIt->next();
9859         // get a new suite of nodes: make replacement
9860         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9861         nodes.resize( nbNodes );
9862         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9863         while ( nIt->more() ) {
9864           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9865           nnIt = nReplaceMap.find( n );
9866           if ( nnIt != nReplaceMap.end() ) {
9867             nbReplaced++;
9868             n = (*nnIt).second;
9869           }
9870           nodes[ i++ ] = n;
9871         }
9872         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9873         //         elemIDsToRemove.push_back( e->GetID() );
9874         //       else
9875         if ( nbReplaced )
9876         {
9877           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9878           aMesh->RemoveElement( e );
9879
9880           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9881           {
9882             AddToSameGroups( newElem, e, aMesh );
9883             if ( int aShapeId = e->getshapeId() )
9884               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9885           }
9886         }
9887       }
9888     }
9889
9890   Remove( nodeIDsToRemove, true );
9891
9892   return aResult;
9893 }
9894
9895 //================================================================================
9896 /*!
9897  * \brief Find corresponding nodes in two sets of faces
9898  * \param theSide1 - first face set
9899  * \param theSide2 - second first face
9900  * \param theFirstNode1 - a boundary node of set 1
9901  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9902  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9903  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9904  * \param nReplaceMap - output map of corresponding nodes
9905  * \return bool  - is a success or not
9906  */
9907 //================================================================================
9908
9909 #ifdef _DEBUG_
9910 //#define DEBUG_MATCHING_NODES
9911 #endif
9912
9913 SMESH_MeshEditor::Sew_Error
9914 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9915                                     set<const SMDS_MeshElement*>& theSide2,
9916                                     const SMDS_MeshNode*          theFirstNode1,
9917                                     const SMDS_MeshNode*          theFirstNode2,
9918                                     const SMDS_MeshNode*          theSecondNode1,
9919                                     const SMDS_MeshNode*          theSecondNode2,
9920                                     TNodeNodeMap &                nReplaceMap)
9921 {
9922   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9923
9924   nReplaceMap.clear();
9925   //if ( theFirstNode1 != theFirstNode2 )
9926   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9927   //if ( theSecondNode1 != theSecondNode2 )
9928   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9929
9930   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9931   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9932
9933   list< NLink > linkList[2];
9934   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9935   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9936
9937   // loop on links in linkList; find faces by links and append links
9938   // of the found faces to linkList
9939   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9940   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9941     NLink link[] = { *linkIt[0], *linkIt[1] };
9942     if ( linkSet.find( link[0] ) == linkSet.end() )
9943       continue;
9944
9945     // by links, find faces in the face sets,
9946     // and find indices of link nodes in the found faces;
9947     // in a face set, there is only one or no face sharing a link
9948     // ---------------------------------------------------------------
9949
9950     const SMDS_MeshElement* face[] = { 0, 0 };
9951     list<const SMDS_MeshNode*> notLinkNodes[2];
9952     //bool reverse[] = { false, false }; // order of notLinkNodes
9953     int nbNodes[2];
9954     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9955     {
9956       const SMDS_MeshNode* n1 = link[iSide].first;
9957       const SMDS_MeshNode* n2 = link[iSide].second;
9958       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9959       set< const SMDS_MeshElement* > facesOfNode1;
9960       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9961       {
9962         // during a loop of the first node, we find all faces around n1,
9963         // during a loop of the second node, we find one face sharing both n1 and n2
9964         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9965         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9966         while ( fIt->more() ) { // loop on faces sharing a node
9967           const SMDS_MeshElement* f = fIt->next();
9968           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9969               ! facesOfNode1.insert( f ).second ) // f encounters twice
9970           {
9971             if ( face[ iSide ] ) {
9972               MESSAGE( "2 faces per link " );
9973               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9974             }
9975             face[ iSide ] = f;
9976             faceSet->erase( f );
9977
9978             // get not link nodes
9979             int nbN = f->NbNodes();
9980             if ( f->IsQuadratic() )
9981               nbN /= 2;
9982             nbNodes[ iSide ] = nbN;
9983             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9984             int i1 = f->GetNodeIndex( n1 );
9985             int i2 = f->GetNodeIndex( n2 );
9986             int iEnd = nbN, iBeg = -1, iDelta = 1;
9987             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9988             if ( reverse ) {
9989               std::swap( iEnd, iBeg ); iDelta = -1;
9990             }
9991             int i = i2;
9992             while ( true ) {
9993               i += iDelta;
9994               if ( i == iEnd ) i = iBeg + iDelta;
9995               if ( i == i1 ) break;
9996               nodes.push_back ( f->GetNode( i ) );
9997             }
9998           }
9999         }
10000       }
10001     }
10002     // check similarity of elements of the sides
10003     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10004       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10005       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10006         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10007       }
10008       else {
10009         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10010       }
10011     }
10012
10013     // set nodes to merge
10014     // -------------------
10015
10016     if ( face[0] && face[1] )  {
10017       if ( nbNodes[0] != nbNodes[1] ) {
10018         MESSAGE("Diff nb of face nodes");
10019         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10020       }
10021 #ifdef DEBUG_MATCHING_NODES
10022       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10023                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10024                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10025 #endif
10026       int nbN = nbNodes[0];
10027       {
10028         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10029         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10030         for ( int i = 0 ; i < nbN - 2; ++i ) {
10031 #ifdef DEBUG_MATCHING_NODES
10032           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10033 #endif
10034           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10035         }
10036       }
10037
10038       // add other links of the face 1 to linkList
10039       // -----------------------------------------
10040
10041       const SMDS_MeshElement* f0 = face[0];
10042       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10043       for ( int i = 0; i < nbN; i++ )
10044       {
10045         const SMDS_MeshNode* n2 = f0->GetNode( i );
10046         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10047           linkSet.insert( SMESH_TLink( n1, n2 ));
10048         if ( !iter_isnew.second ) { // already in a set: no need to process
10049           linkSet.erase( iter_isnew.first );
10050         }
10051         else // new in set == encountered for the first time: add
10052         {
10053 #ifdef DEBUG_MATCHING_NODES
10054           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10055                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10056 #endif
10057           linkList[0].push_back ( NLink( n1, n2 ));
10058           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10059         }
10060         n1 = n2;
10061       }
10062     } // 2 faces found
10063   } // loop on link lists
10064
10065   return SEW_OK;
10066 }
10067
10068 namespace // automatically find theAffectedElems for DoubleNodes()
10069 {
10070   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10071
10072   //--------------------------------------------------------------------------------
10073   // Nodes shared by adjacent FissureBorder's.
10074   // 1 node  if FissureBorder separates faces
10075   // 2 nodes if FissureBorder separates volumes
10076   struct SubBorder
10077   {
10078     const SMDS_MeshNode* _nodes[2];
10079     int                  _nbNodes;
10080
10081     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10082     {
10083       _nodes[0] = n1;
10084       _nodes[1] = n2;
10085       _nbNodes = bool( n1 ) + bool( n2 );
10086       if ( _nbNodes == 2 && n1 > n2 )
10087         std::swap( _nodes[0], _nodes[1] );
10088     }
10089     bool operator<( const SubBorder& other ) const
10090     {
10091       for ( int i = 0; i < _nbNodes; ++i )
10092       {
10093         if ( _nodes[i] < other._nodes[i] ) return true;
10094         if ( _nodes[i] > other._nodes[i] ) return false;
10095       }
10096       return false;
10097     }
10098   };
10099
10100   //--------------------------------------------------------------------------------
10101   // Map a SubBorder to all FissureBorder it bounds
10102   struct FissureBorder;
10103   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10104   typedef TBorderLinks::iterator                               TMappedSub;
10105
10106   //--------------------------------------------------------------------------------
10107   /*!
10108    * \brief Element border (volume facet or face edge) at a fissure
10109    */
10110   struct FissureBorder
10111   {
10112     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10113     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10114
10115     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10116     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10117
10118     FissureBorder( FissureBorder && from ) // move constructor
10119     {
10120       std::swap( _nodes,       from._nodes );
10121       std::swap( _sortedNodes, from._sortedNodes );
10122       _elems[0] = from._elems[0];
10123       _elems[1] = from._elems[1];
10124     }
10125
10126     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10127                    std::vector< const SMDS_MeshElement* > & adjElems)
10128       : _nodes( elemToDuplicate->NbCornerNodes() )
10129     {
10130       for ( size_t i = 0; i < _nodes.size(); ++i )
10131         _nodes[i] = elemToDuplicate->GetNode( i );
10132
10133       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10134       findAdjacent( type, adjElems );
10135     }
10136
10137     FissureBorder( const SMDS_MeshNode**                    nodes,
10138                    const size_t                             nbNodes,
10139                    const SMDSAbs_ElementType                adjElemsType,
10140                    std::vector< const SMDS_MeshElement* > & adjElems)
10141       : _nodes( nodes, nodes + nbNodes )
10142     {
10143       findAdjacent( adjElemsType, adjElems );
10144     }
10145
10146     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10147                        std::vector< const SMDS_MeshElement* > & adjElems)
10148     {
10149       _elems[0] = _elems[1] = 0;
10150       adjElems.clear();
10151       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10152         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10153           _elems[i] = adjElems[i];
10154     }
10155
10156     bool operator<( const FissureBorder& other ) const
10157     {
10158       return GetSortedNodes() < other.GetSortedNodes();
10159     }
10160
10161     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10162     {
10163       if ( _sortedNodes.empty() && !_nodes.empty() )
10164       {
10165         FissureBorder* me = const_cast<FissureBorder*>( this );
10166         me->_sortedNodes = me->_nodes;
10167         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10168       }
10169       return _sortedNodes;
10170     }
10171
10172     size_t NbSub() const
10173     {
10174       return _nodes.size();
10175     }
10176
10177     SubBorder Sub(size_t i) const
10178     {
10179       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10180     }
10181
10182     void AddSelfTo( TBorderLinks& borderLinks )
10183     {
10184       _mappedSubs.resize( NbSub() );
10185       for ( size_t i = 0; i < NbSub(); ++i )
10186       {
10187         TBorderLinks::iterator s2b =
10188           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10189         s2b->second.push_back( this );
10190         _mappedSubs[ i ] = s2b;
10191       }
10192     }
10193
10194     void Clear()
10195     {
10196       _nodes.clear();
10197     }
10198
10199     const SMDS_MeshElement* GetMarkedElem() const
10200     {
10201       if ( _nodes.empty() ) return 0; // cleared
10202       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10203       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10204       return 0;
10205     }
10206
10207     gp_XYZ GetNorm() const // normal to the border
10208     {
10209       gp_XYZ norm;
10210       if ( _nodes.size() == 2 )
10211       {
10212         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10213         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10214           avgNorm += norm;
10215         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10216           avgNorm += norm;
10217
10218         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10219         norm = bordDir ^ avgNorm;
10220       }
10221       else
10222       {
10223         SMESH_NodeXYZ p0( _nodes[0] );
10224         SMESH_NodeXYZ p1( _nodes[1] );
10225         SMESH_NodeXYZ p2( _nodes[2] );
10226         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10227       }
10228       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10229         norm.Reverse();
10230
10231       return norm;
10232     }
10233
10234     void ChooseSide() // mark an _elem located at positive side of fissure
10235     {
10236       _elems[0]->setIsMarked( true );
10237       gp_XYZ norm = GetNorm();
10238       double maxX = norm.Coord(1);
10239       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10240       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10241       if ( maxX < 0 )
10242       {
10243         _elems[0]->setIsMarked( false );
10244         if ( _elems[1] )
10245           _elems[1]->setIsMarked( true );
10246       }
10247     }
10248
10249   }; // struct FissureBorder
10250
10251   //--------------------------------------------------------------------------------
10252   /*!
10253    * \brief Classifier of elements at fissure edge
10254    */
10255   class FissureNormal
10256   {
10257     std::vector< gp_XYZ > _normals;
10258     bool                  _bothIn;
10259
10260   public:
10261     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10262     {
10263       _bothIn = false;
10264       _normals.reserve(2);
10265       _normals.push_back( bord.GetNorm() );
10266       if ( _normals.size() == 2 )
10267         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10268     }
10269
10270     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10271     {
10272       bool isIn = false;
10273       switch ( _normals.size() ) {
10274       case 1:
10275       {
10276         isIn = !isOut( n, _normals[0], elem );
10277         break;
10278       }
10279       case 2:
10280       {
10281         bool in1 = !isOut( n, _normals[0], elem );
10282         bool in2 = !isOut( n, _normals[1], elem );
10283         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10284       }
10285       }
10286       return isIn;
10287     }
10288   };
10289
10290   //================================================================================
10291   /*!
10292    * \brief Classify an element by a plane passing through a node
10293    */
10294   //================================================================================
10295
10296   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10297   {
10298     SMESH_NodeXYZ p = n;
10299     double sumDot = 0;
10300     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10301     {
10302       SMESH_NodeXYZ pi = elem->GetNode( i );
10303       sumDot += norm * ( pi - p );
10304     }
10305     return sumDot < -1e-100;
10306   }
10307
10308   //================================================================================
10309   /*!
10310    * \brief Find FissureBorder's by nodes to duplicate
10311    */
10312   //================================================================================
10313
10314   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10315                            std::vector< FissureBorder > & theFissureBorders )
10316   {
10317     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10318     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10319     if ( !n ) return;
10320     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10321     if ( n->NbInverseElements( elemType ) == 0 )
10322     {
10323       elemType = SMDSAbs_Face;
10324       if ( n->NbInverseElements( elemType ) == 0 )
10325         return;
10326     }
10327     // unmark elements touching the fissure
10328     for ( ; nIt != theNodes.end(); ++nIt )
10329       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10330
10331     // loop on elements touching the fissure to get their borders belonging to the fissure
10332     std::set< FissureBorder >              fissureBorders;
10333     std::vector< const SMDS_MeshElement* > adjElems;
10334     std::vector< const SMDS_MeshNode* >    nodes;
10335     SMDS_VolumeTool volTool;
10336     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10337     {
10338       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10339       while ( invIt->more() )
10340       {
10341         const SMDS_MeshElement* eInv = invIt->next();
10342         if ( eInv->isMarked() ) continue;
10343         eInv->setIsMarked( true );
10344
10345         if ( elemType == SMDSAbs_Volume )
10346         {
10347           volTool.Set( eInv );
10348           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10349           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10350           {
10351             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10352             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10353             nodes.clear();
10354             bool allOnFissure = true;
10355             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10356               if (( allOnFissure = theNodes.count( nn[ iN ])))
10357                 nodes.push_back( nn[ iN ]);
10358             if ( allOnFissure )
10359               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10360                                                                elemType, adjElems )));
10361           }
10362         }
10363         else // elemType == SMDSAbs_Face
10364         {
10365           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10366           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10367           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10368           {
10369             nn[1]      = eInv->GetNode( iN );
10370             onFissure1 = theNodes.count( nn[1] );
10371             if ( onFissure0 && onFissure1 )
10372               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10373             nn[0]      = nn[1];
10374             onFissure0 = onFissure1;
10375           }
10376         }
10377       }
10378     }
10379
10380     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10381     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10382     for ( ; bord != fissureBorders.end(); ++bord )
10383     {
10384       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10385     }
10386     return;
10387   } // findFissureBorders()
10388
10389   //================================================================================
10390   /*!
10391    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10392    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10393    *  \param [in] theNodesNot - nodes not to duplicate
10394    *  \param [out] theAffectedElems - the found elements
10395    */
10396   //================================================================================
10397
10398   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10399                           TIDSortedElemSet&       theAffectedElems)
10400   {
10401     if ( theElemsOrNodes.empty() ) return;
10402
10403     // find FissureBorder's
10404
10405     std::vector< FissureBorder >           fissure;
10406     std::vector< const SMDS_MeshElement* > elemsByFacet;
10407
10408     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10409     if ( (*elIt)->GetType() == SMDSAbs_Node )
10410     {
10411       findFissureBorders( theElemsOrNodes, fissure );
10412     }
10413     else
10414     {
10415       fissure.reserve( theElemsOrNodes.size() );
10416       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10417       {
10418         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10419         if ( !fissure.back()._elems[1] )
10420           fissure.pop_back();
10421       }
10422     }
10423     if ( fissure.empty() )
10424       return;
10425
10426     // fill borderLinks
10427
10428     TBorderLinks borderLinks;
10429
10430     for ( size_t i = 0; i < fissure.size(); ++i )
10431     {
10432       fissure[i].AddSelfTo( borderLinks );
10433     }
10434
10435     // get theAffectedElems
10436
10437     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10438     for ( size_t i = 0; i < fissure.size(); ++i )
10439       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10440       {
10441         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10442                                         false, /*markElem=*/true );
10443       }
10444
10445     std::vector<const SMDS_MeshNode *>                 facetNodes;
10446     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10447     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10448
10449     // choose a side of fissure
10450     fissure[0].ChooseSide();
10451     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10452
10453     size_t nbCheckedBorders = 0;
10454     while ( nbCheckedBorders < fissure.size() )
10455     {
10456       // find a FissureBorder to treat
10457       FissureBorder* bord = 0;
10458       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10459         if ( fissure[i].GetMarkedElem() )
10460           bord = & fissure[i];
10461       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10462         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10463         {
10464           bord = & fissure[i];
10465           bord->ChooseSide();
10466           theAffectedElems.insert( bord->GetMarkedElem() );
10467         }
10468       if ( !bord ) return;
10469       ++nbCheckedBorders;
10470
10471       // treat FissureBorder's linked to bord
10472       fissureNodes.clear();
10473       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10474       for ( size_t i = 0; i < bord->NbSub(); ++i )
10475       {
10476         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10477         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10478         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10479         const SubBorder&                          sb = l2b->first;
10480         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10481
10482         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10483         {
10484           for ( int j = 0; j < sb._nbNodes; ++j )
10485             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10486           continue;
10487         }
10488
10489         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10490         // until an elem adjacent to a neighbour FissureBorder is found
10491         facetNodes.clear();
10492         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10493         facetNodes.resize( sb._nbNodes + 1 );
10494
10495         while ( bordElem )
10496         {
10497           // check if bordElem is adjacent to a neighbour FissureBorder
10498           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10499           {
10500             FissureBorder* bord2 = linkedBorders[j];
10501             if ( bord2 == bord ) continue;
10502             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10503               bordElem = 0;
10504             else
10505               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10506           }
10507           if ( !bordElem )
10508             break;
10509
10510           // find the next bordElem
10511           const SMDS_MeshElement* nextBordElem = 0;
10512           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10513           {
10514             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10515             if ( fissureNodes.count( n )) continue;
10516
10517             facetNodes[ sb._nbNodes ] = n;
10518             elemsByFacet.clear();
10519             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10520             {
10521               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10522                 if ( elemsByFacet[ iE ] != bordElem &&
10523                      !elemsByFacet[ iE ]->isMarked() )
10524                 {
10525                   theAffectedElems.insert( elemsByFacet[ iE ]);
10526                   elemsByFacet[ iE ]->setIsMarked( true );
10527                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10528                     nextBordElem = elemsByFacet[ iE ];
10529                 }
10530             }
10531           }
10532           bordElem = nextBordElem;
10533
10534         } // while ( bordElem )
10535
10536         linkedBorders.clear(); // not to treat this link any more
10537
10538       } // loop on SubBorder's of a FissureBorder
10539
10540       bord->Clear();
10541
10542     } // loop on FissureBorder's
10543
10544
10545     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10546
10547     // mark nodes of theAffectedElems
10548     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10549
10550     // unmark nodes of the fissure
10551     elIt = theElemsOrNodes.begin();
10552     if ( (*elIt)->GetType() == SMDSAbs_Node )
10553       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10554     else
10555       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10556
10557     std::vector< gp_XYZ > normVec;
10558
10559     // loop on nodes of the fissure, add elements having marked nodes
10560     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10561     {
10562       const SMDS_MeshElement* e = (*elIt);
10563       if ( e->GetType() != SMDSAbs_Node )
10564         e->setIsMarked( true ); // avoid adding a fissure element
10565
10566       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10567       {
10568         const SMDS_MeshNode* n = e->GetNode( iN );
10569         if ( fissEdgeNodes2Norm.count( n ))
10570           continue;
10571
10572         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10573         while ( invIt->more() )
10574         {
10575           const SMDS_MeshElement* eInv = invIt->next();
10576           if ( eInv->isMarked() ) continue;
10577           eInv->setIsMarked( true );
10578
10579           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10580           while( nIt->more() )
10581             if ( nIt->next()->isMarked())
10582             {
10583               theAffectedElems.insert( eInv );
10584               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10585               n->setIsMarked( false );
10586               break;
10587             }
10588         }
10589       }
10590     }
10591
10592     // add elements on the fissure edge
10593     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10594     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10595     {
10596       const SMDS_MeshNode* edgeNode = n2N->first;
10597       const FissureNormal & normals = n2N->second;
10598
10599       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10600       while ( invIt->more() )
10601       {
10602         const SMDS_MeshElement* eInv = invIt->next();
10603         if ( eInv->isMarked() ) continue;
10604         eInv->setIsMarked( true );
10605
10606         // classify eInv using normals
10607         bool toAdd = normals.IsIn( edgeNode, eInv );
10608         if ( toAdd ) // check if all nodes lie on the fissure edge
10609         {
10610           bool notOnEdge = false;
10611           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10612             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10613           toAdd = notOnEdge;
10614         }
10615         if ( toAdd )
10616         {
10617           theAffectedElems.insert( eInv );
10618         }
10619       }
10620     }
10621
10622     return;
10623   } // findAffectedElems()
10624 } // namespace
10625
10626 //================================================================================
10627 /*!
10628  * \brief Create elements equal (on same nodes) to given ones
10629  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10630  *              elements of the uppest dimension are duplicated.
10631  */
10632 //================================================================================
10633
10634 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10635 {
10636   ClearLastCreated();
10637   SMESHDS_Mesh* mesh = GetMeshDS();
10638
10639   // get an element type and an iterator over elements
10640
10641   SMDSAbs_ElementType type = SMDSAbs_All;
10642   SMDS_ElemIteratorPtr elemIt;
10643   if ( theElements.empty() )
10644   {
10645     if ( mesh->NbNodes() == 0 )
10646       return;
10647     // get most complex type
10648     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10649       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10650       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10651     };
10652     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10653       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10654       {
10655         type = types[i];
10656         elemIt = mesh->elementsIterator( type );
10657         break;
10658       }
10659   }
10660   else
10661   {
10662     //type = (*theElements.begin())->GetType();
10663     elemIt = SMESHUtils::elemSetIterator( theElements );
10664   }
10665
10666   // un-mark all elements to avoid duplicating just created elements
10667   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10668
10669   // duplicate elements
10670
10671   ElemFeatures elemType;
10672
10673   vector< const SMDS_MeshNode* > nodes;
10674   while ( elemIt->more() )
10675   {
10676     const SMDS_MeshElement* elem = elemIt->next();
10677     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10678         ( elem->isMarked() ))
10679       continue;
10680
10681     elemType.Init( elem, /*basicOnly=*/false );
10682     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10683
10684     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10685       newElem->setIsMarked( true );
10686   }
10687 }
10688
10689 //================================================================================
10690 /*!
10691   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10692   \param theElems - the list of elements (edges or faces) to be replicated
10693   The nodes for duplication could be found from these elements
10694   \param theNodesNot - list of nodes to NOT replicate
10695   \param theAffectedElems - the list of elements (cells and edges) to which the
10696   replicated nodes should be associated to.
10697   \return TRUE if operation has been completed successfully, FALSE otherwise
10698 */
10699 //================================================================================
10700
10701 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10702                                     const TIDSortedElemSet& theNodesNot,
10703                                     const TIDSortedElemSet& theAffectedElems )
10704 {
10705   ClearLastCreated();
10706
10707   if ( theElems.size() == 0 )
10708     return false;
10709
10710   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10711   if ( !aMeshDS )
10712     return false;
10713
10714   bool res = false;
10715   TNodeNodeMap anOldNodeToNewNode;
10716   // duplicate elements and nodes
10717   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10718   // replce nodes by duplications
10719   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10720   return res;
10721 }
10722
10723 //================================================================================
10724 /*!
10725   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10726   \param theMeshDS - mesh instance
10727   \param theElems - the elements replicated or modified (nodes should be changed)
10728   \param theNodesNot - nodes to NOT replicate
10729   \param theNodeNodeMap - relation of old node to new created node
10730   \param theIsDoubleElem - flag os to replicate element or modify
10731   \return TRUE if operation has been completed successfully, FALSE otherwise
10732 */
10733 //================================================================================
10734
10735 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10736                                    const TIDSortedElemSet& theElems,
10737                                    const TIDSortedElemSet& theNodesNot,
10738                                    TNodeNodeMap&           theNodeNodeMap,
10739                                    const bool              theIsDoubleElem )
10740 {
10741   // iterate through element and duplicate them (by nodes duplication)
10742   bool res = false;
10743   std::vector<const SMDS_MeshNode*> newNodes;
10744   ElemFeatures elemType;
10745
10746   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10747   for ( ;  elemItr != theElems.end(); ++elemItr )
10748   {
10749     const SMDS_MeshElement* anElem = *elemItr;
10750     // if (!anElem)
10751     //   continue;
10752
10753     // duplicate nodes to duplicate element
10754     bool isDuplicate = false;
10755     newNodes.resize( anElem->NbNodes() );
10756     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10757     int ind = 0;
10758     while ( anIter->more() )
10759     {
10760       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10761       const SMDS_MeshNode*  aNewNode = aCurrNode;
10762       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10763       if ( n2n != theNodeNodeMap.end() )
10764       {
10765         aNewNode = n2n->second;
10766       }
10767       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10768       {
10769         // duplicate node
10770         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10771         copyPosition( aCurrNode, aNewNode );
10772         theNodeNodeMap[ aCurrNode ] = aNewNode;
10773         myLastCreatedNodes.push_back( aNewNode );
10774       }
10775       isDuplicate |= (aCurrNode != aNewNode);
10776       newNodes[ ind++ ] = aNewNode;
10777     }
10778     if ( !isDuplicate )
10779       continue;
10780
10781     if ( theIsDoubleElem )
10782       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10783     else
10784       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10785
10786     res = true;
10787   }
10788   return res;
10789 }
10790
10791 //================================================================================
10792 /*!
10793   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10794   \param theNodes - identifiers of nodes to be doubled
10795   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10796   nodes. If list of element identifiers is empty then nodes are doubled but
10797   they not assigned to elements
10798   \return TRUE if operation has been completed successfully, FALSE otherwise
10799 */
10800 //================================================================================
10801
10802 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10803                                     const std::list< int >& theListOfModifiedElems )
10804 {
10805   ClearLastCreated();
10806
10807   if ( theListOfNodes.size() == 0 )
10808     return false;
10809
10810   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10811   if ( !aMeshDS )
10812     return false;
10813
10814   // iterate through nodes and duplicate them
10815
10816   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10817
10818   std::list< int >::const_iterator aNodeIter;
10819   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10820   {
10821     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10822     if ( !aNode )
10823       continue;
10824
10825     // duplicate node
10826
10827     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10828     if ( aNewNode )
10829     {
10830       copyPosition( aNode, aNewNode );
10831       anOldNodeToNewNode[ aNode ] = aNewNode;
10832       myLastCreatedNodes.push_back( aNewNode );
10833     }
10834   }
10835
10836   // Change nodes of elements
10837
10838   std::vector<const SMDS_MeshNode*> aNodeArr;
10839
10840   std::list< int >::const_iterator anElemIter;
10841   for ( anElemIter =  theListOfModifiedElems.begin();
10842         anElemIter != theListOfModifiedElems.end();
10843         anElemIter++ )
10844   {
10845     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10846     if ( !anElem )
10847       continue;
10848
10849     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10850     for( size_t i = 0; i < aNodeArr.size(); ++i )
10851     {
10852       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10853         anOldNodeToNewNode.find( aNodeArr[ i ]);
10854       if ( n2n != anOldNodeToNewNode.end() )
10855         aNodeArr[ i ] = n2n->second;
10856     }
10857     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10858   }
10859
10860   return true;
10861 }
10862
10863 namespace {
10864
10865   //================================================================================
10866   /*!
10867     \brief Check if element located inside shape
10868     \return TRUE if IN or ON shape, FALSE otherwise
10869   */
10870   //================================================================================
10871
10872   template<class Classifier>
10873   bool isInside(const SMDS_MeshElement* theElem,
10874                 Classifier&             theClassifier,
10875                 const double            theTol)
10876   {
10877     gp_XYZ centerXYZ (0, 0, 0);
10878     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10879       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10880
10881     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10882     theClassifier.Perform(aPnt, theTol);
10883     TopAbs_State aState = theClassifier.State();
10884     return (aState == TopAbs_IN || aState == TopAbs_ON );
10885   }
10886
10887   //================================================================================
10888   /*!
10889    * \brief Classifier of the 3D point on the TopoDS_Face
10890    *        with interaface suitable for isInside()
10891    */
10892   //================================================================================
10893
10894   struct _FaceClassifier
10895   {
10896     Extrema_ExtPS       _extremum;
10897     BRepAdaptor_Surface _surface;
10898     TopAbs_State        _state;
10899
10900     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10901     {
10902       _extremum.Initialize( _surface,
10903                             _surface.FirstUParameter(), _surface.LastUParameter(),
10904                             _surface.FirstVParameter(), _surface.LastVParameter(),
10905                             _surface.Tolerance(), _surface.Tolerance() );
10906     }
10907     void Perform(const gp_Pnt& aPnt, double theTol)
10908     {
10909       theTol *= theTol;
10910       _state = TopAbs_OUT;
10911       _extremum.Perform(aPnt);
10912       if ( _extremum.IsDone() )
10913         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10914           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10915     }
10916     TopAbs_State State() const
10917     {
10918       return _state;
10919     }
10920   };
10921 }
10922
10923 //================================================================================
10924 /*!
10925   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10926   This method is the first step of DoubleNodeElemGroupsInRegion.
10927   \param theElems - list of groups of elements (edges or faces) to be replicated
10928   \param theNodesNot - list of groups of nodes not to replicate
10929   \param theShape - shape to detect affected elements (element which geometric center
10930          located on or inside shape). If the shape is null, detection is done on faces orientations
10931          (select elements with a gravity center on the side given by faces normals).
10932          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10933          The replicated nodes should be associated to affected elements.
10934   \return true
10935   \sa DoubleNodeElemGroupsInRegion()
10936 */
10937 //================================================================================
10938
10939 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10940                                                    const TIDSortedElemSet& theNodesNot,
10941                                                    const TopoDS_Shape&     theShape,
10942                                                    TIDSortedElemSet&       theAffectedElems)
10943 {
10944   if ( theShape.IsNull() )
10945   {
10946     findAffectedElems( theElems, theAffectedElems );
10947   }
10948   else
10949   {
10950     const double aTol = Precision::Confusion();
10951     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10952     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10953     if ( theShape.ShapeType() == TopAbs_SOLID )
10954     {
10955       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10956       bsc3d->PerformInfinitePoint(aTol);
10957     }
10958     else if (theShape.ShapeType() == TopAbs_FACE )
10959     {
10960       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10961     }
10962
10963     // iterates on indicated elements and get elements by back references from their nodes
10964     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10965     for ( ;  elemItr != theElems.end(); ++elemItr )
10966     {
10967       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10968       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10969       while ( nodeItr->more() )
10970       {
10971         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10972         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10973           continue;
10974         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10975         while ( backElemItr->more() )
10976         {
10977           const SMDS_MeshElement* curElem = backElemItr->next();
10978           if ( curElem && theElems.find(curElem) == theElems.end() &&
10979                ( bsc3d.get() ?
10980                  isInside( curElem, *bsc3d, aTol ) :
10981                  isInside( curElem, *aFaceClassifier, aTol )))
10982             theAffectedElems.insert( curElem );
10983         }
10984       }
10985     }
10986   }
10987   return true;
10988 }
10989
10990 //================================================================================
10991 /*!
10992   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10993   \param theElems - group of of elements (edges or faces) to be replicated
10994   \param theNodesNot - group of nodes not to replicate
10995   \param theShape - shape to detect affected elements (element which geometric center
10996   located on or inside shape).
10997   The replicated nodes should be associated to affected elements.
10998   \return TRUE if operation has been completed successfully, FALSE otherwise
10999 */
11000 //================================================================================
11001
11002 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11003                                             const TIDSortedElemSet& theNodesNot,
11004                                             const TopoDS_Shape&     theShape )
11005 {
11006   if ( theShape.IsNull() )
11007     return false;
11008
11009   const double aTol = Precision::Confusion();
11010   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11011   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11012   if ( theShape.ShapeType() == TopAbs_SOLID )
11013   {
11014     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11015     bsc3d->PerformInfinitePoint(aTol);
11016   }
11017   else if (theShape.ShapeType() == TopAbs_FACE )
11018   {
11019     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11020   }
11021
11022   // iterates on indicated elements and get elements by back references from their nodes
11023   TIDSortedElemSet anAffected;
11024   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11025   for ( ;  elemItr != theElems.end(); ++elemItr )
11026   {
11027     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11028     if (!anElem)
11029       continue;
11030
11031     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11032     while ( nodeItr->more() )
11033     {
11034       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11035       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11036         continue;
11037       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11038       while ( backElemItr->more() )
11039       {
11040         const SMDS_MeshElement* curElem = backElemItr->next();
11041         if ( curElem && theElems.find(curElem) == theElems.end() &&
11042              ( bsc3d ?
11043                isInside( curElem, *bsc3d, aTol ) :
11044                isInside( curElem, *aFaceClassifier, aTol )))
11045           anAffected.insert( curElem );
11046       }
11047     }
11048   }
11049   return DoubleNodes( theElems, theNodesNot, anAffected );
11050 }
11051
11052 /*!
11053  *  \brief compute an oriented angle between two planes defined by four points.
11054  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11055  *  @param p0 base of the rotation axe
11056  *  @param p1 extremity of the rotation axe
11057  *  @param g1 belongs to the first plane
11058  *  @param g2 belongs to the second plane
11059  */
11060 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11061 {
11062   gp_Vec vref(p0, p1);
11063   gp_Vec v1(p0, g1);
11064   gp_Vec v2(p0, g2);
11065   gp_Vec n1 = vref.Crossed(v1);
11066   gp_Vec n2 = vref.Crossed(v2);
11067   try {
11068     return n2.AngleWithRef(n1, vref);
11069   }
11070   catch ( Standard_Failure& ) {
11071   }
11072   return Max( v1.Magnitude(), v2.Magnitude() );
11073 }
11074
11075 /*!
11076  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11077  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11078  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11079  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11080  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11081  * 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.
11082  * 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.
11083  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11084  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11085  * \param theElems - list of groups of volumes, where a group of volume is a set of
11086  *        SMDS_MeshElements sorted by Id.
11087  * \param createJointElems - if TRUE, create the elements
11088  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11089  *        the boundary between \a theDomains and the rest mesh
11090  * \return TRUE if operation has been completed successfully, FALSE otherwise
11091  */
11092 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11093                                                      bool                                 createJointElems,
11094                                                      bool                                 onAllBoundaries)
11095 {
11096   // MESSAGE("----------------------------------------------");
11097   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11098   // MESSAGE("----------------------------------------------");
11099
11100   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11101   meshDS->BuildDownWardConnectivity(true);
11102   CHRONO(50);
11103   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11104
11105   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11106   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11107   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11108
11109   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11110   std::map<int,int>celldom; // cell vtkId --> domain
11111   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11112   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11113   faceDomains.clear();
11114   celldom.clear();
11115   cellDomains.clear();
11116   nodeDomains.clear();
11117   std::map<int,int> emptyMap;
11118   std::set<int> emptySet;
11119   emptyMap.clear();
11120
11121   //MESSAGE(".. Number of domains :"<<theElems.size());
11122
11123   TIDSortedElemSet theRestDomElems;
11124   const int iRestDom  = -1;
11125   const int idom0     = onAllBoundaries ? iRestDom : 0;
11126   const int nbDomains = theElems.size();
11127
11128   // Check if the domains do not share an element
11129   for (int idom = 0; idom < nbDomains-1; idom++)
11130   {
11131     //       MESSAGE("... Check of domain #" << idom);
11132     const TIDSortedElemSet& domain = theElems[idom];
11133     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11134     for (; elemItr != domain.end(); ++elemItr)
11135     {
11136       const SMDS_MeshElement* anElem = *elemItr;
11137       int idombisdeb = idom + 1 ;
11138       // check if the element belongs to a domain further in the list
11139       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11140       {
11141         const TIDSortedElemSet& domainbis = theElems[idombis];
11142         if ( domainbis.count( anElem ))
11143         {
11144           MESSAGE(".... Domain #" << idom);
11145           MESSAGE(".... Domain #" << idombis);
11146           throw SALOME_Exception("The domains are not disjoint.");
11147           return false ;
11148         }
11149       }
11150     }
11151   }
11152
11153   for (int idom = 0; idom < nbDomains; idom++)
11154   {
11155
11156     // --- build a map (face to duplicate --> volume to modify)
11157     //     with all the faces shared by 2 domains (group of elements)
11158     //     and corresponding volume of this domain, for each shared face.
11159     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11160
11161     //MESSAGE("... Neighbors of domain #" << idom);
11162     const TIDSortedElemSet& domain = theElems[idom];
11163     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11164     for (; elemItr != domain.end(); ++elemItr)
11165     {
11166       const SMDS_MeshElement* anElem = *elemItr;
11167       if (!anElem)
11168         continue;
11169       vtkIdType vtkId = anElem->GetVtkID();
11170       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11171       int neighborsVtkIds[NBMAXNEIGHBORS];
11172       int downIds[NBMAXNEIGHBORS];
11173       unsigned char downTypes[NBMAXNEIGHBORS];
11174       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11175       for (int n = 0; n < nbNeighbors; n++)
11176       {
11177         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11178         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11179         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11180         {
11181           bool ok = false;
11182           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11183           {
11184             // MESSAGE("Domain " << idombis);
11185             const TIDSortedElemSet& domainbis = theElems[idombis];
11186             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11187           }
11188           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11189           {
11190             DownIdType face(downIds[n], downTypes[n]);
11191             if (!faceDomains[face].count(idom))
11192             {
11193               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11194               celldom[vtkId] = idom;
11195               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11196             }
11197             if ( !ok )
11198             {
11199               theRestDomElems.insert( elem );
11200               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11201               celldom[neighborsVtkIds[n]] = iRestDom;
11202             }
11203           }
11204         }
11205       }
11206     }
11207   }
11208
11209   //MESSAGE("Number of shared faces " << faceDomains.size());
11210   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11211
11212   // --- explore the shared faces domain by domain,
11213   //     explore the nodes of the face and see if they belong to a cell in the domain,
11214   //     which has only a node or an edge on the border (not a shared face)
11215
11216   for (int idomain = idom0; idomain < nbDomains; idomain++)
11217   {
11218     //MESSAGE("Domain " << idomain);
11219     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11220     itface = faceDomains.begin();
11221     for (; itface != faceDomains.end(); ++itface)
11222     {
11223       const std::map<int, int>& domvol = itface->second;
11224       if (!domvol.count(idomain))
11225         continue;
11226       DownIdType face = itface->first;
11227       //MESSAGE(" --- face " << face.cellId);
11228       std::set<int> oldNodes;
11229       oldNodes.clear();
11230       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11231       std::set<int>::iterator itn = oldNodes.begin();
11232       for (; itn != oldNodes.end(); ++itn)
11233       {
11234         int oldId = *itn;
11235         //MESSAGE("     node " << oldId);
11236         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11237         for (int i=0; i<l.ncells; i++)
11238         {
11239           int vtkId = l.cells[i];
11240           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11241           if (!domain.count(anElem))
11242             continue;
11243           int vtkType = grid->GetCellType(vtkId);
11244           int downId = grid->CellIdToDownId(vtkId);
11245           if (downId < 0)
11246           {
11247             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11248             continue; // not OK at this stage of the algorithm:
11249             //no cells created after BuildDownWardConnectivity
11250           }
11251           DownIdType aCell(downId, vtkType);
11252           cellDomains[aCell][idomain] = vtkId;
11253           celldom[vtkId] = idomain;
11254           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11255         }
11256       }
11257     }
11258   }
11259
11260   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11261   //     for each shared face, get the nodes
11262   //     for each node, for each domain of the face, create a clone of the node
11263
11264   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11265   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11266   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11267
11268   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11269   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11270   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11271
11272   //MESSAGE(".. Duplication of the nodes");
11273   for (int idomain = idom0; idomain < nbDomains; idomain++)
11274   {
11275     itface = faceDomains.begin();
11276     for (; itface != faceDomains.end(); ++itface)
11277     {
11278       const std::map<int, int>& domvol = itface->second;
11279       if (!domvol.count(idomain))
11280         continue;
11281       DownIdType face = itface->first;
11282       //MESSAGE(" --- face " << face.cellId);
11283       std::set<int> oldNodes;
11284       oldNodes.clear();
11285       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11286       std::set<int>::iterator itn = oldNodes.begin();
11287       for (; itn != oldNodes.end(); ++itn)
11288       {
11289         int oldId = *itn;
11290         if (nodeDomains[oldId].empty())
11291         {
11292           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11293           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11294         }
11295         std::map<int, int>::const_iterator itdom = domvol.begin();
11296         for (; itdom != domvol.end(); ++itdom)
11297         {
11298           int idom = itdom->first;
11299           //MESSAGE("         domain " << idom);
11300           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11301           {
11302             if (nodeDomains[oldId].size() >= 2) // a multiple node
11303             {
11304               vector<int> orderedDoms;
11305               //MESSAGE("multiple node " << oldId);
11306               if (mutipleNodes.count(oldId))
11307                 orderedDoms = mutipleNodes[oldId];
11308               else
11309               {
11310                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11311                 for (; it != nodeDomains[oldId].end(); ++it)
11312                   orderedDoms.push_back(it->first);
11313               }
11314               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11315               //stringstream txt;
11316               //for (int i=0; i<orderedDoms.size(); i++)
11317               //  txt << orderedDoms[i] << " ";
11318               //MESSAGE("orderedDoms " << txt.str());
11319               mutipleNodes[oldId] = orderedDoms;
11320             }
11321             double *coords = grid->GetPoint(oldId);
11322             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11323             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11324             int newId = newNode->GetVtkID();
11325             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11326             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11327           }
11328         }
11329       }
11330     }
11331   }
11332
11333   //MESSAGE(".. Creation of elements");
11334   for (int idomain = idom0; idomain < nbDomains; idomain++)
11335   {
11336     itface = faceDomains.begin();
11337     for (; itface != faceDomains.end(); ++itface)
11338     {
11339       std::map<int, int> domvol = itface->second;
11340       if (!domvol.count(idomain))
11341         continue;
11342       DownIdType face = itface->first;
11343       //MESSAGE(" --- face " << face.cellId);
11344       std::set<int> oldNodes;
11345       oldNodes.clear();
11346       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11347       int nbMultipleNodes = 0;
11348       std::set<int>::iterator itn = oldNodes.begin();
11349       for (; itn != oldNodes.end(); ++itn)
11350       {
11351         int oldId = *itn;
11352         if (mutipleNodes.count(oldId))
11353           nbMultipleNodes++;
11354       }
11355       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11356       {
11357         //MESSAGE("multiple Nodes detected on a shared face");
11358         int downId = itface->first.cellId;
11359         unsigned char cellType = itface->first.cellType;
11360         // --- shared edge or shared face ?
11361         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11362         {
11363           int nodes[3];
11364           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11365           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11366             if (mutipleNodes.count(nodes[i]))
11367               if (!mutipleNodesToFace.count(nodes[i]))
11368                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11369         }
11370         else // shared face (between two volumes)
11371         {
11372           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11373           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11374           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11375           for (int ie =0; ie < nbEdges; ie++)
11376           {
11377             int nodes[3];
11378             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11379             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11380             {
11381               vector<int> vn0 = mutipleNodes[nodes[0]];
11382               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11383               vector<int> doms;
11384               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11385                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11386                   if ( vn0[i0] == vn1[i1] )
11387                     doms.push_back( vn0[ i0 ]);
11388               if ( doms.size() > 2 )
11389               {
11390                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11391                 double *coords = grid->GetPoint(nodes[0]);
11392                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11393                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11394                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11395                 gp_Pnt gref;
11396                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11397                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11398                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11399                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11400                 for ( size_t id = 0; id < doms.size(); id++ )
11401                 {
11402                   int idom = doms[id];
11403                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11404                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11405                   {
11406                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11407                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11408                     if (domain.count(elem))
11409                     {
11410                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11411                       domvol[idom] = (SMDS_MeshVolume*) svol;
11412                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11413                       double values[3] = { 0,0,0 };
11414                       vtkIdType npts = 0;
11415                       vtkIdType const *pts(nullptr);
11416                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11417                       for ( vtkIdType i = 0; i < npts; ++i )
11418                       {
11419                         double *coords = grid->GetPoint( pts[i] );
11420                         for ( int j = 0; j < 3; ++j )
11421                           values[j] += coords[j] / npts;
11422                       }
11423                       if ( id == 0 )
11424                       {
11425                         gref.SetCoord( values[0], values[1], values[2] );
11426                         angleDom[idom] = 0;
11427                       }
11428                       else
11429                       {
11430                         gp_Pnt g( values[0], values[1], values[2] );
11431                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11432                         //MESSAGE("  angle=" << angleDom[idom]);
11433                       }
11434                       break;
11435                     }
11436                   }
11437                 }
11438                 map<double, int> sortedDom; // sort domains by angle
11439                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11440                   sortedDom[ia->second] = ia->first;
11441                 vector<int> vnodes;
11442                 vector<int> vdom;
11443                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11444                 {
11445                   vdom.push_back(ib->second);
11446                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11447                 }
11448                 for (int ino = 0; ino < nbNodes; ino++)
11449                   vnodes.push_back(nodes[ino]);
11450                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11451               }
11452             }
11453           }
11454         }
11455       }
11456     }
11457   }
11458
11459   // --- iterate on shared faces (volumes to modify, face to extrude)
11460   //     get node id's of the face (id SMDS = id VTK)
11461   //     create flat element with old and new nodes if requested
11462
11463   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11464   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11465
11466   std::map<int, std::map<long,int> > nodeQuadDomains;
11467   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11468
11469   //MESSAGE(".. Creation of elements: simple junction");
11470   if (createJointElems)
11471   {
11472     string joints2DName = "joints2D";
11473     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11474     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11475     string joints3DName = "joints3D";
11476     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11477     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11478
11479     itface = faceDomains.begin();
11480     for (; itface != faceDomains.end(); ++itface)
11481     {
11482       DownIdType face = itface->first;
11483       std::set<int> oldNodes;
11484       std::set<int>::iterator itn;
11485       oldNodes.clear();
11486       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11487
11488       std::map<int, int> domvol = itface->second;
11489       std::map<int, int>::iterator itdom = domvol.begin();
11490       int dom1 = itdom->first;
11491       int vtkVolId = itdom->second;
11492       itdom++;
11493       int dom2 = itdom->first;
11494       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11495                                                        nodeQuadDomains);
11496       stringstream grpname;
11497       grpname << "j_";
11498       if (dom1 < dom2)
11499         grpname << dom1 << "_" << dom2;
11500       else
11501         grpname << dom2 << "_" << dom1;
11502       string namegrp = grpname.str();
11503       if (!mapOfJunctionGroups.count(namegrp))
11504         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11505       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11506       if (sgrp)
11507         sgrp->Add(vol->GetID());
11508       if (vol->GetType() == SMDSAbs_Volume)
11509         joints3DGrp->Add(vol->GetID());
11510       else if (vol->GetType() == SMDSAbs_Face)
11511         joints2DGrp->Add(vol->GetID());
11512     }
11513   }
11514
11515   // --- create volumes on multiple domain intersection if requested
11516   //     iterate on mutipleNodesToFace
11517   //     iterate on edgesMultiDomains
11518
11519   //MESSAGE(".. Creation of elements: multiple junction");
11520   if (createJointElems)
11521   {
11522     // --- iterate on mutipleNodesToFace
11523
11524     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11525     for (; itn != mutipleNodesToFace.end(); ++itn)
11526     {
11527       int node = itn->first;
11528       vector<int> orderDom = itn->second;
11529       vector<vtkIdType> orderedNodes;
11530       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11531         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11532       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11533
11534       stringstream grpname;
11535       grpname << "m2j_";
11536       grpname << 0 << "_" << 0;
11537       string namegrp = grpname.str();
11538       if (!mapOfJunctionGroups.count(namegrp))
11539         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11540       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11541       if (sgrp)
11542         sgrp->Add(face->GetID());
11543     }
11544
11545     // --- iterate on edgesMultiDomains
11546
11547     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11548     for (; ite != edgesMultiDomains.end(); ++ite)
11549     {
11550       vector<int> nodes = ite->first;
11551       vector<int> orderDom = ite->second;
11552       vector<vtkIdType> orderedNodes;
11553       if (nodes.size() == 2)
11554       {
11555         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11556         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11557           if ( orderDom.size() == 3 )
11558             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11559               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11560           else
11561             for (int idom = orderDom.size()-1; idom >=0; idom--)
11562               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11563         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11564
11565         string namegrp = "jointsMultiples";
11566         if (!mapOfJunctionGroups.count(namegrp))
11567           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11568         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11569         if (sgrp)
11570           sgrp->Add(vol->GetID());
11571       }
11572       else
11573       {
11574         //INFOS("Quadratic multiple joints not implemented");
11575         // TODO quadratic nodes
11576       }
11577     }
11578   }
11579
11580   // --- list the explicit faces and edges of the mesh that need to be modified,
11581   //     i.e. faces and edges built with one or more duplicated nodes.
11582   //     associate these faces or edges to their corresponding domain.
11583   //     only the first domain found is kept when a face or edge is shared
11584
11585   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11586   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11587   faceOrEdgeDom.clear();
11588   feDom.clear();
11589
11590   //MESSAGE(".. Modification of elements");
11591   for (int idomain = idom0; idomain < nbDomains; idomain++)
11592   {
11593     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11594     for (; itnod != nodeDomains.end(); ++itnod)
11595     {
11596       int oldId = itnod->first;
11597       //MESSAGE("     node " << oldId);
11598       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11599       for (int i = 0; i < l.ncells; i++)
11600       {
11601         int vtkId = l.cells[i];
11602         int vtkType = grid->GetCellType(vtkId);
11603         int downId = grid->CellIdToDownId(vtkId);
11604         if (downId < 0)
11605           continue; // new cells: not to be modified
11606         DownIdType aCell(downId, vtkType);
11607         int volParents[1000];
11608         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11609         for (int j = 0; j < nbvol; j++)
11610           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11611             if (!feDom.count(vtkId))
11612             {
11613               feDom[vtkId] = idomain;
11614               faceOrEdgeDom[aCell] = emptyMap;
11615               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11616               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11617               //        << " type " << vtkType << " downId " << downId);
11618             }
11619       }
11620     }
11621   }
11622
11623   // --- iterate on shared faces (volumes to modify, face to extrude)
11624   //     get node id's of the face
11625   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11626
11627   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11628   for (int m=0; m<3; m++)
11629   {
11630     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11631     itface = (*amap).begin();
11632     for (; itface != (*amap).end(); ++itface)
11633     {
11634       DownIdType face = itface->first;
11635       std::set<int> oldNodes;
11636       std::set<int>::iterator itn;
11637       oldNodes.clear();
11638       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11639       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11640       std::map<int, int> localClonedNodeIds;
11641
11642       std::map<int, int> domvol = itface->second;
11643       std::map<int, int>::iterator itdom = domvol.begin();
11644       for (; itdom != domvol.end(); ++itdom)
11645       {
11646         int idom = itdom->first;
11647         int vtkVolId = itdom->second;
11648         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11649         localClonedNodeIds.clear();
11650         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11651         {
11652           int oldId = *itn;
11653           if (nodeDomains[oldId].count(idom))
11654           {
11655             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11656             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11657           }
11658         }
11659         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11660       }
11661     }
11662   }
11663
11664   // Remove empty groups (issue 0022812)
11665   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11666   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11667   {
11668     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11669       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11670   }
11671
11672   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11673   grid->DeleteLinks();
11674
11675   CHRONOSTOP(50);
11676   counters::stats();
11677   return true;
11678 }
11679
11680 /*!
11681  * \brief Double nodes on some external faces and create flat elements.
11682  * Flat elements are mainly used by some types of mechanic calculations.
11683  *
11684  * Each group of the list must be constituted of faces.
11685  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11686  * @param theElems - list of groups of faces, where a group of faces is a set of
11687  * SMDS_MeshElements sorted by Id.
11688  * @return TRUE if operation has been completed successfully, FALSE otherwise
11689  */
11690 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11691 {
11692   // MESSAGE("-------------------------------------------------");
11693   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11694   // MESSAGE("-------------------------------------------------");
11695
11696   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11697
11698   // --- For each group of faces
11699   //     duplicate the nodes, create a flat element based on the face
11700   //     replace the nodes of the faces by their clones
11701
11702   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11703   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11704   clonedNodes.clear();
11705   intermediateNodes.clear();
11706   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11707   mapOfJunctionGroups.clear();
11708
11709   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11710   {
11711     const TIDSortedElemSet&           domain = theElems[idom];
11712     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11713     for ( ; elemItr != domain.end(); ++elemItr )
11714     {
11715       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11716       if (!aFace)
11717         continue;
11718       // MESSAGE("aFace=" << aFace->GetID());
11719       bool isQuad = aFace->IsQuadratic();
11720       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11721
11722       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11723
11724       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11725       while (nodeIt->more())
11726       {
11727         const SMDS_MeshNode* node = nodeIt->next();
11728         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11729         if (isMedium)
11730           ln2.push_back(node);
11731         else
11732           ln0.push_back(node);
11733
11734         const SMDS_MeshNode* clone = 0;
11735         if (!clonedNodes.count(node))
11736         {
11737           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11738           copyPosition( node, clone );
11739           clonedNodes[node] = clone;
11740         }
11741         else
11742           clone = clonedNodes[node];
11743
11744         if (isMedium)
11745           ln3.push_back(clone);
11746         else
11747           ln1.push_back(clone);
11748
11749         const SMDS_MeshNode* inter = 0;
11750         if (isQuad && (!isMedium))
11751         {
11752           if (!intermediateNodes.count(node))
11753           {
11754             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11755             copyPosition( node, inter );
11756             intermediateNodes[node] = inter;
11757           }
11758           else
11759             inter = intermediateNodes[node];
11760           ln4.push_back(inter);
11761         }
11762       }
11763
11764       // --- extrude the face
11765
11766       vector<const SMDS_MeshNode*> ln;
11767       SMDS_MeshVolume* vol = 0;
11768       vtkIdType aType = aFace->GetVtkType();
11769       switch (aType)
11770       {
11771       case VTK_TRIANGLE:
11772         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11773         // MESSAGE("vol prism " << vol->GetID());
11774         ln.push_back(ln1[0]);
11775         ln.push_back(ln1[1]);
11776         ln.push_back(ln1[2]);
11777         break;
11778       case VTK_QUAD:
11779         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11780         // MESSAGE("vol hexa " << vol->GetID());
11781         ln.push_back(ln1[0]);
11782         ln.push_back(ln1[1]);
11783         ln.push_back(ln1[2]);
11784         ln.push_back(ln1[3]);
11785         break;
11786       case VTK_QUADRATIC_TRIANGLE:
11787         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11788                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11789         // MESSAGE("vol quad prism " << vol->GetID());
11790         ln.push_back(ln1[0]);
11791         ln.push_back(ln1[1]);
11792         ln.push_back(ln1[2]);
11793         ln.push_back(ln3[0]);
11794         ln.push_back(ln3[1]);
11795         ln.push_back(ln3[2]);
11796         break;
11797       case VTK_QUADRATIC_QUAD:
11798         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11799         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11800         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11801         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11802                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11803                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11804         // MESSAGE("vol quad hexa " << vol->GetID());
11805         ln.push_back(ln1[0]);
11806         ln.push_back(ln1[1]);
11807         ln.push_back(ln1[2]);
11808         ln.push_back(ln1[3]);
11809         ln.push_back(ln3[0]);
11810         ln.push_back(ln3[1]);
11811         ln.push_back(ln3[2]);
11812         ln.push_back(ln3[3]);
11813         break;
11814       case VTK_POLYGON:
11815         break;
11816       default:
11817         break;
11818       }
11819
11820       if (vol)
11821       {
11822         stringstream grpname;
11823         grpname << "jf_";
11824         grpname << idom;
11825         string namegrp = grpname.str();
11826         if (!mapOfJunctionGroups.count(namegrp))
11827           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11828         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11829         if (sgrp)
11830           sgrp->Add(vol->GetID());
11831       }
11832
11833       // --- modify the face
11834
11835       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11836     }
11837   }
11838   return true;
11839 }
11840
11841 /*!
11842  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11843  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11844  *  groups of faces to remove inside the object, (idem edges).
11845  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11846  */
11847 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11848                                       const TopoDS_Shape&             theShape,
11849                                       SMESH_NodeSearcher*             theNodeSearcher,
11850                                       const char*                     groupName,
11851                                       std::vector<double>&            nodesCoords,
11852                                       std::vector<std::vector<int> >& listOfListOfNodes)
11853 {
11854   // MESSAGE("--------------------------------");
11855   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11856   // MESSAGE("--------------------------------");
11857
11858   // --- zone of volumes to remove is given :
11859   //     1 either by a geom shape (one or more vertices) and a radius,
11860   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11861   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11862   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11863   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11864   //     defined by it's name.
11865
11866   SMESHDS_GroupBase* groupDS = 0;
11867   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11868   while ( groupIt->more() )
11869   {
11870     groupDS = 0;
11871     SMESH_Group * group = groupIt->next();
11872     if ( !group ) continue;
11873     groupDS = group->GetGroupDS();
11874     if ( !groupDS || groupDS->IsEmpty() ) continue;
11875     std::string grpName = group->GetName();
11876     //MESSAGE("grpName=" << grpName);
11877     if (grpName == groupName)
11878       break;
11879     else
11880       groupDS = 0;
11881   }
11882
11883   bool isNodeGroup = false;
11884   bool isNodeCoords = false;
11885   if (groupDS)
11886   {
11887     if (groupDS->GetType() != SMDSAbs_Node)
11888       return;
11889     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11890   }
11891
11892   if (nodesCoords.size() > 0)
11893     isNodeCoords = true; // a list o nodes given by their coordinates
11894   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11895
11896   // --- define groups to build
11897
11898   // --- group of SMDS volumes
11899   string grpvName = groupName;
11900   grpvName += "_vol";
11901   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11902   if (!grp)
11903   {
11904     MESSAGE("group not created " << grpvName);
11905     return;
11906   }
11907   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11908
11909   // --- group of SMDS faces on the skin
11910   string grpsName = groupName;
11911   grpsName += "_skin";
11912   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11913   if (!grps)
11914   {
11915     MESSAGE("group not created " << grpsName);
11916     return;
11917   }
11918   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11919
11920   // --- group of SMDS faces internal (several shapes)
11921   string grpiName = groupName;
11922   grpiName += "_internalFaces";
11923   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11924   if (!grpi)
11925   {
11926     MESSAGE("group not created " << grpiName);
11927     return;
11928   }
11929   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11930
11931   // --- group of SMDS faces internal (several shapes)
11932   string grpeiName = groupName;
11933   grpeiName += "_internalEdges";
11934   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11935   if (!grpei)
11936   {
11937     MESSAGE("group not created " << grpeiName);
11938     return;
11939   }
11940   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11941
11942   // --- build downward connectivity
11943
11944   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11945   meshDS->BuildDownWardConnectivity(true);
11946   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11947
11948   // --- set of volumes detected inside
11949
11950   std::set<int> setOfInsideVol;
11951   std::set<int> setOfVolToCheck;
11952
11953   std::vector<gp_Pnt> gpnts;
11954   gpnts.clear();
11955
11956   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11957   {
11958     //MESSAGE("group of nodes provided");
11959     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11960     while ( elemIt->more() )
11961     {
11962       const SMDS_MeshElement* elem = elemIt->next();
11963       if (!elem)
11964         continue;
11965       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11966       if (!node)
11967         continue;
11968       SMDS_MeshElement* vol = 0;
11969       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11970       while (volItr->more())
11971       {
11972         vol = (SMDS_MeshElement*)volItr->next();
11973         setOfInsideVol.insert(vol->GetVtkID());
11974         sgrp->Add(vol->GetID());
11975       }
11976     }
11977   }
11978   else if (isNodeCoords)
11979   {
11980     //MESSAGE("list of nodes coordinates provided");
11981     size_t i = 0;
11982     int k = 0;
11983     while ( i < nodesCoords.size()-2 )
11984     {
11985       double x = nodesCoords[i++];
11986       double y = nodesCoords[i++];
11987       double z = nodesCoords[i++];
11988       gp_Pnt p = gp_Pnt(x, y ,z);
11989       gpnts.push_back(p);
11990       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11991       k++;
11992     }
11993   }
11994   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11995   {
11996     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11997     TopTools_IndexedMapOfShape vertexMap;
11998     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11999     gp_Pnt p = gp_Pnt(0,0,0);
12000     if (vertexMap.Extent() < 1)
12001       return;
12002
12003     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12004     {
12005       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12006       p = BRep_Tool::Pnt(vertex);
12007       gpnts.push_back(p);
12008       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12009     }
12010   }
12011
12012   if (gpnts.size() > 0)
12013   {
12014     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12015     //MESSAGE("startNode->nodeId " << nodeId);
12016
12017     double radius2 = radius*radius;
12018     //MESSAGE("radius2 " << radius2);
12019
12020     // --- volumes on start node
12021
12022     setOfVolToCheck.clear();
12023     SMDS_MeshElement* startVol = 0;
12024     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12025     while (volItr->more())
12026     {
12027       startVol = (SMDS_MeshElement*)volItr->next();
12028       setOfVolToCheck.insert(startVol->GetVtkID());
12029     }
12030     if (setOfVolToCheck.empty())
12031     {
12032       MESSAGE("No volumes found");
12033       return;
12034     }
12035
12036     // --- starting with central volumes then their neighbors, check if they are inside
12037     //     or outside the domain, until no more new neighbor volume is inside.
12038     //     Fill the group of inside volumes
12039
12040     std::map<int, double> mapOfNodeDistance2;
12041     mapOfNodeDistance2.clear();
12042     std::set<int> setOfOutsideVol;
12043     while (!setOfVolToCheck.empty())
12044     {
12045       std::set<int>::iterator it = setOfVolToCheck.begin();
12046       int vtkId = *it;
12047       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12048       bool volInside = false;
12049       vtkIdType npts = 0;
12050       vtkIdType const *pts(nullptr);
12051       grid->GetCellPoints(vtkId, npts, pts);
12052       for (int i=0; i<npts; i++)
12053       {
12054         double distance2 = 0;
12055         if (mapOfNodeDistance2.count(pts[i]))
12056         {
12057           distance2 = mapOfNodeDistance2[pts[i]];
12058           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12059         }
12060         else
12061         {
12062           double *coords = grid->GetPoint(pts[i]);
12063           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12064           distance2 = 1.E40;
12065           for ( size_t j = 0; j < gpnts.size(); j++ )
12066           {
12067             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12068             if (d2 < distance2)
12069             {
12070               distance2 = d2;
12071               if (distance2 < radius2)
12072                 break;
12073             }
12074           }
12075           mapOfNodeDistance2[pts[i]] = distance2;
12076           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12077         }
12078         if (distance2 < radius2)
12079         {
12080           volInside = true; // one or more nodes inside the domain
12081           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12082           break;
12083         }
12084       }
12085       if (volInside)
12086       {
12087         setOfInsideVol.insert(vtkId);
12088         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12089         int neighborsVtkIds[NBMAXNEIGHBORS];
12090         int downIds[NBMAXNEIGHBORS];
12091         unsigned char downTypes[NBMAXNEIGHBORS];
12092         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12093         for (int n = 0; n < nbNeighbors; n++)
12094           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12095             setOfVolToCheck.insert(neighborsVtkIds[n]);
12096       }
12097       else
12098       {
12099         setOfOutsideVol.insert(vtkId);
12100         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12101       }
12102       setOfVolToCheck.erase(vtkId);
12103     }
12104   }
12105
12106   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12107   //     If yes, add the volume to the inside set
12108
12109   bool addedInside = true;
12110   std::set<int> setOfVolToReCheck;
12111   while (addedInside)
12112   {
12113     //MESSAGE(" --------------------------- re check");
12114     addedInside = false;
12115     std::set<int>::iterator itv = setOfInsideVol.begin();
12116     for (; itv != setOfInsideVol.end(); ++itv)
12117     {
12118       int vtkId = *itv;
12119       int neighborsVtkIds[NBMAXNEIGHBORS];
12120       int downIds[NBMAXNEIGHBORS];
12121       unsigned char downTypes[NBMAXNEIGHBORS];
12122       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12123       for (int n = 0; n < nbNeighbors; n++)
12124         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12125           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12126     }
12127     setOfVolToCheck = setOfVolToReCheck;
12128     setOfVolToReCheck.clear();
12129     while  (!setOfVolToCheck.empty())
12130     {
12131       std::set<int>::iterator it = setOfVolToCheck.begin();
12132       int vtkId = *it;
12133       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12134       {
12135         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12136         int countInside = 0;
12137         int neighborsVtkIds[NBMAXNEIGHBORS];
12138         int downIds[NBMAXNEIGHBORS];
12139         unsigned char downTypes[NBMAXNEIGHBORS];
12140         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12141         for (int n = 0; n < nbNeighbors; n++)
12142           if (setOfInsideVol.count(neighborsVtkIds[n]))
12143             countInside++;
12144         //MESSAGE("countInside " << countInside);
12145         if (countInside > 1)
12146         {
12147           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12148           setOfInsideVol.insert(vtkId);
12149           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12150           addedInside = true;
12151         }
12152         else
12153           setOfVolToReCheck.insert(vtkId);
12154       }
12155       setOfVolToCheck.erase(vtkId);
12156     }
12157   }
12158
12159   // --- map of Downward faces at the boundary, inside the global volume
12160   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12161   //     fill group of SMDS faces inside the volume (when several volume shapes)
12162   //     fill group of SMDS faces on the skin of the global volume (if skin)
12163
12164   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12165   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12166   std::set<int>::iterator it = setOfInsideVol.begin();
12167   for (; it != setOfInsideVol.end(); ++it)
12168   {
12169     int vtkId = *it;
12170     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12171     int neighborsVtkIds[NBMAXNEIGHBORS];
12172     int downIds[NBMAXNEIGHBORS];
12173     unsigned char downTypes[NBMAXNEIGHBORS];
12174     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12175     for (int n = 0; n < nbNeighbors; n++)
12176     {
12177       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12178       if (neighborDim == 3)
12179       {
12180         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12181         {
12182           DownIdType face(downIds[n], downTypes[n]);
12183           boundaryFaces[face] = vtkId;
12184         }
12185         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12186         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12187         if (vtkFaceId >= 0)
12188         {
12189           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12190           // find also the smds edges on this face
12191           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12192           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12193           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12194           for (int i = 0; i < nbEdges; i++)
12195           {
12196             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12197             if (vtkEdgeId >= 0)
12198               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12199           }
12200         }
12201       }
12202       else if (neighborDim == 2) // skin of the volume
12203       {
12204         DownIdType face(downIds[n], downTypes[n]);
12205         skinFaces[face] = vtkId;
12206         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12207         if (vtkFaceId >= 0)
12208           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12209       }
12210     }
12211   }
12212
12213   // --- identify the edges constituting the wire of each subshape on the skin
12214   //     define polylines with the nodes of edges, equivalent to wires
12215   //     project polylines on subshapes, and partition, to get geom faces
12216
12217   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12218   std::set<int> emptySet;
12219   emptySet.clear();
12220   std::set<int> shapeIds;
12221
12222   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12223   while (itelem->more())
12224   {
12225     const SMDS_MeshElement *elem = itelem->next();
12226     int shapeId = elem->getshapeId();
12227     int   vtkId = elem->GetVtkID();
12228     if (!shapeIdToVtkIdSet.count(shapeId))
12229     {
12230       shapeIdToVtkIdSet[shapeId] = emptySet;
12231       shapeIds.insert(shapeId);
12232     }
12233     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12234   }
12235
12236   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12237   std::set<DownIdType, DownIdCompare> emptyEdges;
12238   emptyEdges.clear();
12239
12240   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12241   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12242   {
12243     int shapeId = itShape->first;
12244     //MESSAGE(" --- Shape ID --- "<< shapeId);
12245     shapeIdToEdges[shapeId] = emptyEdges;
12246
12247     std::vector<int> nodesEdges;
12248
12249     std::set<int>::iterator its = itShape->second.begin();
12250     for (; its != itShape->second.end(); ++its)
12251     {
12252       int vtkId = *its;
12253       //MESSAGE("     " << vtkId);
12254       int neighborsVtkIds[NBMAXNEIGHBORS];
12255       int downIds[NBMAXNEIGHBORS];
12256       unsigned char downTypes[NBMAXNEIGHBORS];
12257       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12258       for (int n = 0; n < nbNeighbors; n++)
12259       {
12260         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12261           continue;
12262         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12263         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12264         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12265         {
12266           DownIdType edge(downIds[n], downTypes[n]);
12267           if (!shapeIdToEdges[shapeId].count(edge))
12268           {
12269             shapeIdToEdges[shapeId].insert(edge);
12270             int vtkNodeId[3];
12271             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12272             nodesEdges.push_back(vtkNodeId[0]);
12273             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12274             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12275           }
12276         }
12277       }
12278     }
12279
12280     std::list<int> order;
12281     order.clear();
12282     if (nodesEdges.size() > 0)
12283     {
12284       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12285       nodesEdges[0] = -1;
12286       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12287       nodesEdges[1] = -1; // do not reuse this edge
12288       bool found = true;
12289       while (found)
12290       {
12291         int nodeTofind = order.back(); // try first to push back
12292         int i = 0;
12293         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12294           if (nodesEdges[i] == nodeTofind)
12295             break;
12296         if ( i == (int) nodesEdges.size() )
12297           found = false; // no follower found on back
12298         else
12299         {
12300           if (i%2) // odd ==> use the previous one
12301             if (nodesEdges[i-1] < 0)
12302               found = false;
12303             else
12304             {
12305               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12306               nodesEdges[i-1] = -1;
12307             }
12308           else // even ==> use the next one
12309             if (nodesEdges[i+1] < 0)
12310               found = false;
12311             else
12312             {
12313               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12314               nodesEdges[i+1] = -1;
12315             }
12316         }
12317         if (found)
12318           continue;
12319         // try to push front
12320         found = true;
12321         nodeTofind = order.front(); // try to push front
12322         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12323           if ( nodesEdges[i] == nodeTofind )
12324             break;
12325         if ( i == (int)nodesEdges.size() )
12326         {
12327           found = false; // no predecessor found on front
12328           continue;
12329         }
12330         if (i%2) // odd ==> use the previous one
12331           if (nodesEdges[i-1] < 0)
12332             found = false;
12333           else
12334           {
12335             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12336             nodesEdges[i-1] = -1;
12337           }
12338         else // even ==> use the next one
12339           if (nodesEdges[i+1] < 0)
12340             found = false;
12341           else
12342           {
12343             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12344             nodesEdges[i+1] = -1;
12345           }
12346       }
12347     }
12348
12349
12350     std::vector<int> nodes;
12351     nodes.push_back(shapeId);
12352     std::list<int>::iterator itl = order.begin();
12353     for (; itl != order.end(); itl++)
12354     {
12355       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12356       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12357     }
12358     listOfListOfNodes.push_back(nodes);
12359   }
12360
12361   //     partition geom faces with blocFissure
12362   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12363   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12364
12365   return;
12366 }
12367
12368
12369 //================================================================================
12370 /*!
12371  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12372  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12373  * \return TRUE if operation has been completed successfully, FALSE otherwise
12374  */
12375 //================================================================================
12376
12377 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12378 {
12379   // iterates on volume elements and detect all free faces on them
12380   SMESHDS_Mesh* aMesh = GetMeshDS();
12381   if (!aMesh)
12382     return false;
12383
12384   ElemFeatures faceType( SMDSAbs_Face );
12385   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12386   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12387   while(vIt->more())
12388   {
12389     const SMDS_MeshVolume* volume = vIt->next();
12390     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12391     vTool.SetExternalNormal();
12392     const int iQuad = volume->IsQuadratic();
12393     faceType.SetQuad( iQuad );
12394     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12395     {
12396       if (!vTool.IsFreeFace(iface))
12397         continue;
12398       nbFree++;
12399       vector<const SMDS_MeshNode *> nodes;
12400       int nbFaceNodes = vTool.NbFaceNodes(iface);
12401       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12402       int inode = 0;
12403       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12404         nodes.push_back(faceNodes[inode]);
12405
12406       if (iQuad) // add medium nodes
12407       {
12408         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12409           nodes.push_back(faceNodes[inode]);
12410         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12411           nodes.push_back(faceNodes[8]);
12412       }
12413       // add new face based on volume nodes
12414       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12415       {
12416         nbExisted++; // face already exists
12417       }
12418       else
12419       {
12420         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12421         nbCreated++;
12422       }
12423     }
12424   }
12425   return ( nbFree == ( nbExisted + nbCreated ));
12426 }
12427
12428 namespace
12429 {
12430   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12431   {
12432     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12433       return n;
12434     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12435   }
12436 }
12437 //================================================================================
12438 /*!
12439  * \brief Creates missing boundary elements
12440  *  \param elements - elements whose boundary is to be checked
12441  *  \param dimension - defines type of boundary elements to create
12442  *  \param group - a group to store created boundary elements in
12443  *  \param targetMesh - a mesh to store created boundary elements in
12444  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12445  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12446  *                                boundary elements will be copied into the targetMesh
12447  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12448  *                                boundary elements will be added into the new group
12449  *  \param aroundElements - if true, elements will be created on boundary of given
12450  *                          elements else, on boundary of the whole mesh.
12451  * \return nb of added boundary elements
12452  */
12453 //================================================================================
12454
12455 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12456                                        Bnd_Dimension           dimension,
12457                                        SMESH_Group*            group/*=0*/,
12458                                        SMESH_Mesh*             targetMesh/*=0*/,
12459                                        bool                    toCopyElements/*=false*/,
12460                                        bool                    toCopyExistingBoundary/*=false*/,
12461                                        bool                    toAddExistingBondary/*= false*/,
12462                                        bool                    aroundElements/*= false*/)
12463 {
12464   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12465   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12466   // hope that all elements are of the same type, do not check them all
12467   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12468     throw SALOME_Exception(LOCALIZED("wrong element type"));
12469
12470   if ( !targetMesh )
12471     toCopyElements = toCopyExistingBoundary = false;
12472
12473   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12474   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12475   int nbAddedBnd = 0;
12476
12477   // editor adding present bnd elements and optionally holding elements to add to the group
12478   SMESH_MeshEditor* presentEditor;
12479   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12480   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12481
12482   SMESH_MesherHelper helper( *myMesh );
12483   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12484   SMDS_VolumeTool vTool;
12485   TIDSortedElemSet avoidSet;
12486   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12487   size_t inode;
12488
12489   typedef vector<const SMDS_MeshNode*> TConnectivity;
12490   TConnectivity tgtNodes;
12491   ElemFeatures elemKind( missType ), elemToCopy;
12492
12493   vector<const SMDS_MeshElement*> presentBndElems;
12494   vector<TConnectivity>           missingBndElems;
12495   vector<int>                     freeFacets;
12496   TConnectivity nodes, elemNodes;
12497
12498   SMDS_ElemIteratorPtr eIt;
12499   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12500   else                  eIt = SMESHUtils::elemSetIterator( elements );
12501
12502   while ( eIt->more() )
12503   {
12504     const SMDS_MeshElement* elem = eIt->next();
12505     const int              iQuad = elem->IsQuadratic();
12506     elemKind.SetQuad( iQuad );
12507
12508     // ------------------------------------------------------------------------------------
12509     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12510     // ------------------------------------------------------------------------------------
12511     presentBndElems.clear();
12512     missingBndElems.clear();
12513     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12514     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12515     {
12516       const SMDS_MeshElement* otherVol = 0;
12517       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12518       {
12519         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12520              ( !aroundElements || elements.count( otherVol )))
12521           continue;
12522         freeFacets.push_back( iface );
12523       }
12524       if ( missType == SMDSAbs_Face )
12525         vTool.SetExternalNormal();
12526       for ( size_t i = 0; i < freeFacets.size(); ++i )
12527       {
12528         int                iface = freeFacets[i];
12529         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12530         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12531         if ( missType == SMDSAbs_Edge ) // boundary edges
12532         {
12533           nodes.resize( 2+iQuad );
12534           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12535           {
12536             for ( size_t j = 0; j < nodes.size(); ++j )
12537               nodes[ j ] = nn[ i+j ];
12538             if ( const SMDS_MeshElement* edge =
12539                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12540               presentBndElems.push_back( edge );
12541             else
12542               missingBndElems.push_back( nodes );
12543           }
12544         }
12545         else // boundary face
12546         {
12547           nodes.clear();
12548           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12549             nodes.push_back( nn[inode] ); // add corner nodes
12550           if (iQuad)
12551             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12552               nodes.push_back( nn[inode] ); // add medium nodes
12553           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12554           if ( iCenter > 0 )
12555             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12556
12557           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12558                                                                SMDSAbs_Face, /*noMedium=*/false ))
12559             presentBndElems.push_back( f );
12560           else
12561             missingBndElems.push_back( nodes );
12562
12563           if ( targetMesh != myMesh )
12564           {
12565             // add 1D elements on face boundary to be added to a new mesh
12566             const SMDS_MeshElement* edge;
12567             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12568             {
12569               if ( iQuad )
12570                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12571               else
12572                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12573               if ( edge && avoidSet.insert( edge ).second )
12574                 presentBndElems.push_back( edge );
12575             }
12576           }
12577         }
12578       }
12579     }
12580     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12581     {
12582       avoidSet.clear(), avoidSet.insert( elem );
12583       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12584                         SMDS_MeshElement::iterator() );
12585       elemNodes.push_back( elemNodes[0] );
12586       nodes.resize( 2 + iQuad );
12587       const int nbLinks = elem->NbCornerNodes();
12588       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12589       {
12590         nodes[0] = elemNodes[iN];
12591         nodes[1] = elemNodes[iN+1+iQuad];
12592         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12593           continue; // not free link
12594
12595         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12596         if ( const SMDS_MeshElement* edge =
12597              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12598           presentBndElems.push_back( edge );
12599         else
12600           missingBndElems.push_back( nodes );
12601       }
12602     }
12603
12604     // ---------------------------------
12605     // 2. Add missing boundary elements
12606     // ---------------------------------
12607     if ( targetMesh != myMesh )
12608       // instead of making a map of nodes in this mesh and targetMesh,
12609       // we create nodes with same IDs.
12610       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12611       {
12612         TConnectivity& srcNodes = missingBndElems[i];
12613         tgtNodes.resize( srcNodes.size() );
12614         for ( inode = 0; inode < srcNodes.size(); ++inode )
12615           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12616         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12617                                                                        missType,
12618                                                                        /*noMedium=*/false))
12619           continue;
12620         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12621         ++nbAddedBnd;
12622       }
12623     else
12624       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12625       {
12626         TConnectivity& nodes = missingBndElems[ i ];
12627         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12628                                                                        missType,
12629                                                                        /*noMedium=*/false))
12630           continue;
12631         SMDS_MeshElement* newElem =
12632           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12633         nbAddedBnd += bool( newElem );
12634
12635         // try to set a new element to a shape
12636         if ( myMesh->HasShapeToMesh() )
12637         {
12638           bool ok = true;
12639           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12640           const size_t nbN = nodes.size() / (iQuad+1 );
12641           for ( inode = 0; inode < nbN && ok; ++inode )
12642           {
12643             pair<int, TopAbs_ShapeEnum> i_stype =
12644               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12645             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12646               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12647           }
12648           if ( ok && mediumShapes.size() > 1 )
12649           {
12650             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12651             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12652             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12653             {
12654               if (( ok = ( stype_i->first != stype_i_0.first )))
12655                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12656                                         aMesh->IndexToShape( stype_i_0.second ));
12657             }
12658           }
12659           if ( ok && mediumShapes.begin()->first == missShapeType )
12660             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12661         }
12662       }
12663
12664     // ----------------------------------
12665     // 3. Copy present boundary elements
12666     // ----------------------------------
12667     if ( toCopyExistingBoundary )
12668       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12669       {
12670         const SMDS_MeshElement* e = presentBndElems[i];
12671         tgtNodes.resize( e->NbNodes() );
12672         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12673           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12674         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12675       }
12676     else // store present elements to add them to a group
12677       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12678       {
12679         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12680       }
12681
12682   } // loop on given elements
12683
12684   // ---------------------------------------------
12685   // 4. Fill group with boundary elements
12686   // ---------------------------------------------
12687   if ( group )
12688   {
12689     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12690       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12691         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12692   }
12693   tgtEditor.myLastCreatedElems.clear();
12694   tgtEditor2.myLastCreatedElems.clear();
12695
12696   // -----------------------
12697   // 5. Copy given elements
12698   // -----------------------
12699   if ( toCopyElements && targetMesh != myMesh )
12700   {
12701     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12702     else                  eIt = SMESHUtils::elemSetIterator( elements );
12703     while (eIt->more())
12704     {
12705       const SMDS_MeshElement* elem = eIt->next();
12706       tgtNodes.resize( elem->NbNodes() );
12707       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12708         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12709       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12710
12711       tgtEditor.myLastCreatedElems.clear();
12712     }
12713   }
12714   return nbAddedBnd;
12715 }
12716
12717 //================================================================================
12718 /*!
12719  * \brief Copy node position and set \a to node on the same geometry
12720  */
12721 //================================================================================
12722
12723 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12724                                      const SMDS_MeshNode* to )
12725 {
12726   if ( !from || !to ) return;
12727
12728   SMDS_PositionPtr pos = from->GetPosition();
12729   if ( !pos || from->getshapeId() < 1 ) return;
12730
12731   switch ( pos->GetTypeOfPosition() )
12732   {
12733   case SMDS_TOP_3DSPACE: break;
12734
12735   case SMDS_TOP_FACE:
12736   {
12737     SMDS_FacePositionPtr fPos = pos;
12738     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12739                                 fPos->GetUParameter(), fPos->GetVParameter() );
12740     break;
12741   }
12742   case SMDS_TOP_EDGE:
12743   {
12744     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12745     SMDS_EdgePositionPtr ePos = pos;
12746     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12747     break;
12748   }
12749   case SMDS_TOP_VERTEX:
12750   {
12751     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12752     break;
12753   }
12754   case SMDS_TOP_UNSPEC:
12755   default:;
12756   }
12757 }