Salome HOME
Update of CheckDone
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2020  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 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 //=======================================================================
109 //function : SMESH_MeshEditor
110 //purpose  :
111 //=======================================================================
112
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114   :myMesh( theMesh ) // theMesh may be NULL
115 {
116 }
117
118 //================================================================================
119 /*!
120  * \brief Return mesh DS
121  */
122 //================================================================================
123
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 {
126   return myMesh->GetMeshDS();
127 }
128
129
130 //================================================================================
131 /*!
132  * \brief Clears myLastCreatedNodes and myLastCreatedElems
133  */
134 //================================================================================
135
136 void SMESH_MeshEditor::ClearLastCreated()
137 {
138   SMESHUtils::FreeVector( myLastCreatedElems );
139   SMESHUtils::FreeVector( myLastCreatedNodes );
140 }
141
142 //================================================================================
143 /*!
144  * \brief Initializes members by an existing element
145  *  \param [in] elem - the source element
146  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147  */
148 //================================================================================
149
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
152 {
153   if ( elem )
154   {
155     myType = elem->GetType();
156     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157     {
158       myIsPoly = elem->IsPoly();
159       if ( myIsPoly )
160       {
161         myIsQuad = elem->IsQuadratic();
162         if ( myType == SMDSAbs_Volume && !basicOnly )
163         {
164           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165         }
166       }
167     }
168     else if ( myType == SMDSAbs_Ball && !basicOnly )
169     {
170       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
171     }
172   }
173   return *this;
174 }
175
176 //=======================================================================
177 /*!
178  * \brief Add element
179  */
180 //=======================================================================
181
182 SMDS_MeshElement*
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184                              const ElemFeatures&                  features)
185 {
186   SMDS_MeshElement* e = 0;
187   int nbnode = node.size();
188   SMESHDS_Mesh* mesh = GetMeshDS();
189   const int ID = features.myID;
190
191   switch ( features.myType ) {
192   case SMDSAbs_Face:
193     if ( !features.myIsPoly ) {
194       if      (nbnode == 3) {
195         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196         else           e = mesh->AddFace      (node[0], node[1], node[2] );
197       }
198       else if (nbnode == 4) {
199         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204                                                node[4], node[5], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
206                                                node[4], node[5] );
207       }
208       else if (nbnode == 7) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210                                                node[4], node[5], node[6], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6] );
213       }
214       else if (nbnode == 8) {
215         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216                                                node[4], node[5], node[6], node[7], ID);
217         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7] );
219       }
220       else if (nbnode == 9) {
221         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], node[7], node[8], ID);
223         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8] );
225       }
226     }
227     else if ( !features.myIsQuad )
228     {
229       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230       else           e = mesh->AddPolygonalFace      (node    );
231     }
232     else if ( nbnode % 2 == 0 ) // just a protection
233     {
234       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235       else           e = mesh->AddQuadPolygonalFace      (node    );
236     }
237     break;
238
239   case SMDSAbs_Volume:
240     if ( !features.myIsPoly ) {
241       if      (nbnode == 4) {
242         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
244       }
245       else if (nbnode == 5) {
246         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247                                                  node[4], ID);
248         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
249                                                  node[4] );
250       }
251       else if (nbnode == 6) {
252         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253                                                  node[4], node[5], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5] );
256       }
257       else if (nbnode == 8) {
258         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259                                                  node[4], node[5], node[6], node[7], ID);
260         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7] );
262       }
263       else if (nbnode == 10) {
264         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7],
269                                                  node[8], node[9] );
270       }
271       else if (nbnode == 12) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10], node[11], ID);
275         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], node[10], node[11] );
278       }
279       else if (nbnode == 13) {
280         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281                                                  node[4], node[5], node[6], node[7],
282                                                  node[8], node[9], node[10],node[11],
283                                                  node[12],ID);
284         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10],node[11],
287                                                  node[12] );
288       }
289       else if (nbnode == 15) {
290         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291                                                  node[4], node[5], node[6], node[7],
292                                                  node[8], node[9], node[10],node[11],
293                                                  node[12],node[13],node[14],ID);
294         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],node[13],node[14] );
298       }
299       else if (nbnode == 18) {
300         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301                                                  node[4], node[5], node[6], node[7],
302                                                  node[8], node[9], node[10],node[11],
303                                                  node[12],node[13],node[14],
304                                                  node[15],node[16],node[17],ID );
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14],
309                                                  node[15],node[16],node[17] );
310       }
311       else if (nbnode == 20) {
312         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313                                                  node[4], node[5], node[6], node[7],
314                                                  node[8], node[9], node[10],node[11],
315                                                  node[12],node[13],node[14],node[15],
316                                                  node[16],node[17],node[18],node[19],ID);
317         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
318                                                  node[4], node[5], node[6], node[7],
319                                                  node[8], node[9], node[10],node[11],
320                                                  node[12],node[13],node[14],node[15],
321                                                  node[16],node[17],node[18],node[19] );
322       }
323       else if (nbnode == 27) {
324         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325                                                  node[4], node[5], node[6], node[7],
326                                                  node[8], node[9], node[10],node[11],
327                                                  node[12],node[13],node[14],node[15],
328                                                  node[16],node[17],node[18],node[19],
329                                                  node[20],node[21],node[22],node[23],
330                                                  node[24],node[25],node[26], ID);
331         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
332                                                  node[4], node[5], node[6], node[7],
333                                                  node[8], node[9], node[10],node[11],
334                                                  node[12],node[13],node[14],node[15],
335                                                  node[16],node[17],node[18],node[19],
336                                                  node[20],node[21],node[22],node[23],
337                                                  node[24],node[25],node[26] );
338       }
339     }
340     else if ( !features.myIsQuad )
341     {
342       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
344     }
345     else
346     {
347       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
349     }
350     break;
351
352   case SMDSAbs_Edge:
353     if ( nbnode == 2 ) {
354       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355       else           e = mesh->AddEdge      (node[0], node[1] );
356     }
357     else if ( nbnode == 3 ) {
358       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
360     }
361     break;
362
363   case SMDSAbs_0DElement:
364     if ( nbnode == 1 ) {
365       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366       else           e = mesh->Add0DElement      (node[0] );
367     }
368     break;
369
370   case SMDSAbs_Node:
371     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
373     break;
374
375   case SMDSAbs_Ball:
376     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
378     break;
379
380   default:;
381   }
382   if ( e ) myLastCreatedElems.push_back( e );
383   return e;
384 }
385
386 //=======================================================================
387 /*!
388  * \brief Add element
389  */
390 //=======================================================================
391
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393                                                const ElemFeatures& features)
394 {
395   vector<const SMDS_MeshNode*> nodes;
396   nodes.reserve( nodeIDs.size() );
397   vector<int>::const_iterator id = nodeIDs.begin();
398   while ( id != nodeIDs.end() ) {
399     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400       nodes.push_back( node );
401     else
402       return 0;
403   }
404   return AddElement( nodes, features );
405 }
406
407 //=======================================================================
408 //function : Remove
409 //purpose  : Remove a node or an element.
410 //           Modify a compute state of sub-meshes which become empty
411 //=======================================================================
412
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
414                               const bool         isNodes )
415 {
416   ClearLastCreated();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = SMESHUtils::elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.back() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   ClearLastCreated();
527
528   SMESHDS_Mesh * aMesh = GetMeshDS();
529   if ( aMesh->ShapeToMesh().IsNull() )
530     return 0;
531
532   int aShapeID = theElem->getshapeId();
533   if ( aShapeID < 1 )
534     return 0;
535
536   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537     if ( sm->Contains( theElem ))
538       return aShapeID;
539
540   if ( theElem->GetType() == SMDSAbs_Node ) {
541     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
542   }
543   else {
544     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
545   }
546
547   TopoDS_Shape aShape; // the shape a node of theElem is on
548   if ( theElem->GetType() != SMDSAbs_Node )
549   {
550     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551     while ( nodeIt->more() ) {
552       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553       if ((aShapeID = node->getshapeId()) > 0) {
554         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555           if ( sm->Contains( theElem ))
556             return aShapeID;
557           if ( aShape.IsNull() )
558             aShape = aMesh->IndexToShape( aShapeID );
559         }
560       }
561     }
562   }
563
564   // None of nodes is on a proper shape,
565   // find the shape among ancestors of aShape on which a node is
566   if ( !aShape.IsNull() ) {
567     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568     for ( ; ancIt.More(); ancIt.Next() ) {
569       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570       if ( sm && sm->Contains( theElem ))
571         return aMesh->ShapeToIndex( ancIt.Value() );
572     }
573   }
574   else
575   {
576     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577     while ( const SMESHDS_SubMesh* sm = smIt->next() )
578       if ( sm->Contains( theElem ))
579         return sm->GetID();
580   }
581
582   return 0;
583 }
584
585 //=======================================================================
586 //function : IsMedium
587 //purpose  :
588 //=======================================================================
589
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
591                                 const SMDSAbs_ElementType typeToCheck)
592 {
593   bool isMedium = false;
594   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595   while (it->more() && !isMedium ) {
596     const SMDS_MeshElement* elem = it->next();
597     isMedium = elem->IsMediumNode(node);
598   }
599   return isMedium;
600 }
601
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose  : Shift nodes in the array corresponded to quadratic triangle
605 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
607
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
609 {
610   const SMDS_MeshNode* nd1 = aNodes[0];
611   aNodes[0] = aNodes[1];
612   aNodes[1] = aNodes[2];
613   aNodes[2] = nd1;
614   const SMDS_MeshNode* nd2 = aNodes[3];
615   aNodes[3] = aNodes[4];
616   aNodes[4] = aNodes[5];
617   aNodes[5] = nd2;
618 }
619
620 //=======================================================================
621 //function : getNodesFromTwoTria
622 //purpose  : 
623 //=======================================================================
624
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626                                 const SMDS_MeshElement * theTria2,
627                                 vector< const SMDS_MeshNode*>& N1,
628                                 vector< const SMDS_MeshNode*>& N2)
629 {
630   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631   if ( N1.size() < 6 ) return false;
632   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633   if ( N2.size() < 6 ) return false;
634
635   int sames[3] = {-1,-1,-1};
636   int nbsames = 0;
637   int i, j;
638   for(i=0; i<3; i++) {
639     for(j=0; j<3; j++) {
640       if(N1[i]==N2[j]) {
641         sames[i] = j;
642         nbsames++;
643         break;
644       }
645     }
646   }
647   if(nbsames!=2) return false;
648   if(sames[0]>-1) {
649     shiftNodesQuadTria(N1);
650     if(sames[1]>-1) {
651       shiftNodesQuadTria(N1);
652     }
653   }
654   i = sames[0] + sames[1] + sames[2];
655   for(; i<2; i++) {
656     shiftNodesQuadTria(N2);
657   }
658   // now we receive following N1 and N2 (using numeration as in the image below)
659   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
660   // i.e. first nodes from both arrays form a new diagonal
661   return true;
662 }
663
664 //=======================================================================
665 //function : InverseDiag
666 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
667 //           but having other common link.
668 //           Return False if args are improper
669 //=======================================================================
670
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672                                     const SMDS_MeshElement * theTria2 )
673 {
674   ClearLastCreated();
675
676   if ( !theTria1 || !theTria2 ||
677        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679        theTria1->GetType() != SMDSAbs_Face ||
680        theTria2->GetType() != SMDSAbs_Face )
681     return false;
682
683   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684       (theTria2->GetEntityType() == SMDSEntity_Triangle))
685   {
686     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
688     //    |/ |                                         | \|
689     //  B +--+ 2                                     B +--+ 2
690
691     // put nodes in array and find out indices of the same ones
692     const SMDS_MeshNode* aNodes [6];
693     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
694     int i = 0;
695     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696     while ( it->more() ) {
697       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
698
699       if ( i > 2 ) // theTria2
700         // find same node of theTria1
701         for ( int j = 0; j < 3; j++ )
702           if ( aNodes[ i ] == aNodes[ j ]) {
703             sameInd[ j ] = i;
704             sameInd[ i ] = j;
705             break;
706           }
707       // next
708       i++;
709       if ( i == 3 ) {
710         if ( it->more() )
711           return false; // theTria1 is not a triangle
712         it = theTria2->nodesIterator();
713       }
714       if ( i == 6 && it->more() )
715         return false; // theTria2 is not a triangle
716     }
717
718     // find indices of 1,2 and of A,B in theTria1
719     int iA = -1, iB = 0, i1 = 0, i2 = 0;
720     for ( i = 0; i < 6; i++ ) {
721       if ( sameInd [ i ] == -1 ) {
722         if ( i < 3 ) i1 = i;
723         else         i2 = i;
724       }
725       else if (i < 3) {
726         if ( iA >= 0) iB = i;
727         else          iA = i;
728       }
729     }
730     // nodes 1 and 2 should not be the same
731     if ( aNodes[ i1 ] == aNodes[ i2 ] )
732       return false;
733
734     // theTria1: A->2
735     aNodes[ iA ] = aNodes[ i2 ];
736     // theTria2: B->1
737     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
738
739     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
741
742     return true;
743
744   } // end if(F1 && F2)
745
746   // check case of quadratic faces
747   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
749     return false;
750   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
752     return false;
753
754   //       5
755   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
757   //    |   / |
758   //  7 +  +  + 6
759   //    | /9  |
760   //    |/    |
761   //  4 +--+--+ 3
762   //       8
763
764   vector< const SMDS_MeshNode* > N1;
765   vector< const SMDS_MeshNode* > N2;
766   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
767     return false;
768   // now we receive following N1 and N2 (using numeration as above image)
769   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
770   // i.e. first nodes from both arrays determ new diagonal
771
772   vector< const SMDS_MeshNode*> N1new( N1.size() );
773   vector< const SMDS_MeshNode*> N2new( N2.size() );
774   N1new.back() = N1.back(); // central node of biquadratic
775   N2new.back() = N2.back();
776   N1new[0] = N1[0];  N2new[0] = N1[0];
777   N1new[1] = N2[0];  N2new[1] = N1[1];
778   N1new[2] = N2[1];  N2new[2] = N2[0];
779   N1new[3] = N1[4];  N2new[3] = N1[3];
780   N1new[4] = N2[3];  N2new[4] = N2[5];
781   N1new[5] = N1[5];  N2new[5] = N1[4];
782   // change nodes in faces
783   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
785
786   // move the central node of biquadratic triangle
787   SMESH_MesherHelper helper( *GetMesh() );
788   for ( int is2nd = 0; is2nd < 2; ++is2nd )
789   {
790     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
791     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792     if ( nodes.size() < 7 )
793       continue;
794     helper.SetSubShape( tria->getshapeId() );
795     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
796     gp_Pnt xyz;
797     if ( F.IsNull() )
798     {
799       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800               SMESH_NodeXYZ( nodes[4] ) +
801               SMESH_NodeXYZ( nodes[5] )) / 3.;
802     }
803     else
804     {
805       bool checkUV;
806       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
809       TopLoc_Location loc;
810       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811       xyz = S->Value( uv.X(), uv.Y() );
812       xyz.Transform( loc );
813       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
814            nodes[6]->getshapeId() > 0 )
815         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
816     }
817     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
818   }
819   return true;
820 }
821
822 //=======================================================================
823 //function : findTriangles
824 //purpose  : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
826
827 static bool findTriangles(const SMDS_MeshNode *    theNode1,
828                           const SMDS_MeshNode *    theNode2,
829                           const SMDS_MeshElement*& theTria1,
830                           const SMDS_MeshElement*& theTria2)
831 {
832   if ( !theNode1 || !theNode2 ) return false;
833
834   theTria1 = theTria2 = 0;
835
836   set< const SMDS_MeshElement* > emap;
837   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
838   while (it->more()) {
839     const SMDS_MeshElement* elem = it->next();
840     if ( elem->NbCornerNodes() == 3 )
841       emap.insert( elem );
842   }
843   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
844   while (it->more()) {
845     const SMDS_MeshElement* elem = it->next();
846     if ( emap.count( elem )) {
847       if ( !theTria1 )
848       {
849         theTria1 = elem;
850       }
851       else  
852       {
853         theTria2 = elem;
854         // theTria1 must be element with minimum ID
855         if ( theTria2->GetID() < theTria1->GetID() )
856           std::swap( theTria2, theTria1 );
857         return true;
858       }
859     }
860   }
861   return false;
862 }
863
864 //=======================================================================
865 //function : InverseDiag
866 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
867 //           with ones built on the same 4 nodes but having other common link.
868 //           Return false if proper faces not found
869 //=======================================================================
870
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872                                     const SMDS_MeshNode * theNode2)
873 {
874   ClearLastCreated();
875
876   const SMDS_MeshElement *tr1, *tr2;
877   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
878     return false;
879
880   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
882     return false;
883
884   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
886
887     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
889     //    |/ |                                    | \|
890     //  B +--+ 2                                B +--+ 2
891
892     // put nodes in array
893     // and find indices of 1,2 and of A in tr1 and of B in tr2
894     int i, iA1 = 0, i1 = 0;
895     const SMDS_MeshNode* aNodes1 [3];
896     SMDS_ElemIteratorPtr it;
897     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899       if ( aNodes1[ i ] == theNode1 )
900         iA1 = i; // node A in tr1
901       else if ( aNodes1[ i ] != theNode2 )
902         i1 = i;  // node 1
903     }
904     int iB2 = 0, i2 = 0;
905     const SMDS_MeshNode* aNodes2 [3];
906     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes2[ i ] == theNode2 )
909         iB2 = i; // node B in tr2
910       else if ( aNodes2[ i ] != theNode1 )
911         i2 = i;  // node 2
912     }
913
914     // nodes 1 and 2 should not be the same
915     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
916       return false;
917
918     // tr1: A->2
919     aNodes1[ iA1 ] = aNodes2[ i2 ];
920     // tr2: B->1
921     aNodes2[ iB2 ] = aNodes1[ i1 ];
922
923     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
925
926     return true;
927   }
928
929   // check case of quadratic faces
930   return InverseDiag(tr1,tr2);
931 }
932
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
936 //           fusion of triangles tr1 and tr2 having shared link on
937 //           theNode1 and theNode2
938 //=======================================================================
939
940 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
941                         const SMDS_MeshNode *    theNode1,
942                         const SMDS_MeshNode *    theNode2,
943                         const SMDS_MeshElement * tr1,
944                         const SMDS_MeshElement * tr2 )
945 {
946   if( tr1->NbNodes() != tr2->NbNodes() )
947     return false;
948   // find the 4-th node to insert into tr1
949   const SMDS_MeshNode* n4 = 0;
950   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
951   int i=0;
952   while ( !n4 && i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( !isDiag )
957       n4 = n;
958   }
959   // Make an array of nodes to be in a quadrangle
960   int iNode = 0, iFirstDiag = -1;
961   it = tr1->nodesIterator();
962   i=0;
963   while ( i<3 ) {
964     const SMDS_MeshNode * n = cast2Node( it->next() );
965     i++;
966     bool isDiag = ( n == theNode1 || n == theNode2 );
967     if ( isDiag ) {
968       if ( iFirstDiag < 0 )
969         iFirstDiag = iNode;
970       else if ( iNode - iFirstDiag == 1 )
971         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
972     }
973     else if ( n == n4 ) {
974       return false; // tr1 and tr2 should not have all the same nodes
975     }
976     theQuadNodes[ iNode++ ] = n;
977   }
978   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979     theQuadNodes[ iNode ] = n4;
980
981   return true;
982 }
983
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
987 //           with a quadrangle built on the same 4 nodes.
988 //           Return false if proper faces not found
989 //=======================================================================
990
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992                                    const SMDS_MeshNode * theNode2)
993 {
994   ClearLastCreated();
995
996   const SMDS_MeshElement *tr1, *tr2;
997   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
998     return false;
999
1000   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1002     return false;
1003
1004   SMESHDS_Mesh * aMesh = GetMeshDS();
1005
1006   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007       (tr2->GetEntityType() == SMDSEntity_Triangle))
1008   {
1009     const SMDS_MeshNode* aNodes [ 4 ];
1010     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1011       return false;
1012
1013     const SMDS_MeshElement* newElem = 0;
1014     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015     myLastCreatedElems.push_back(newElem);
1016     AddToSameGroups( newElem, tr1, aMesh );
1017     int aShapeId = tr1->getshapeId();
1018     if ( aShapeId )
1019       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1020
1021     aMesh->RemoveElement( tr1 );
1022     aMesh->RemoveElement( tr2 );
1023
1024     return true;
1025   }
1026
1027   // check case of quadratic faces
1028   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1029     return false;
1030   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1031     return false;
1032
1033   //       5
1034   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1036   //    |   / |
1037   //  7 +  +  + 6
1038   //    | /9  |
1039   //    |/    |
1040   //  4 +--+--+ 3
1041   //       8
1042
1043   vector< const SMDS_MeshNode* > N1;
1044   vector< const SMDS_MeshNode* > N2;
1045   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1046     return false;
1047   // now we receive following N1 and N2 (using numeration as above image)
1048   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1049   // i.e. first nodes from both arrays determ new diagonal
1050
1051   const SMDS_MeshNode* aNodes[8];
1052   aNodes[0] = N1[0];
1053   aNodes[1] = N1[1];
1054   aNodes[2] = N2[0];
1055   aNodes[3] = N2[1];
1056   aNodes[4] = N1[3];
1057   aNodes[5] = N2[5];
1058   aNodes[6] = N2[3];
1059   aNodes[7] = N1[5];
1060
1061   const SMDS_MeshElement* newElem = 0;
1062   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064   myLastCreatedElems.push_back(newElem);
1065   AddToSameGroups( newElem, tr1, aMesh );
1066   int aShapeId = tr1->getshapeId();
1067   if ( aShapeId )
1068   {
1069     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1070   }
1071   aMesh->RemoveElement( tr1 );
1072   aMesh->RemoveElement( tr2 );
1073
1074   // remove middle node (9)
1075   GetMeshDS()->RemoveNode( N1[4] );
1076
1077   return true;
1078 }
1079
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose  : Reverse theElement orientation
1083 //=======================================================================
1084
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1086 {
1087   ClearLastCreated();
1088
1089   if (!theElem)
1090     return false;
1091   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092   if ( !it || !it->more() )
1093     return false;
1094
1095   const SMDSAbs_ElementType type = theElem->GetType();
1096   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1097     return false;
1098
1099   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1101   {
1102     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1103     if (!aPolyedre) {
1104       MESSAGE("Warning: bad volumic element");
1105       return false;
1106     }
1107     SMDS_VolumeTool vTool( aPolyedre );
1108     const int nbFaces = vTool.NbFaces();
1109     vector<int> quantities( nbFaces );
1110     vector<const SMDS_MeshNode *> poly_nodes;
1111
1112     // check if all facets are oriented equally
1113     bool sameOri = true;
1114     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115     for (int iface = 0; iface < nbFaces; iface++)
1116     {
1117       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118       if ( facetOri[ iface ] != facetOri[ 0 ])
1119         sameOri = false;
1120     }
1121
1122     // reverse faces of the polyhedron
1123     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124     poly_nodes.reserve( vTool.NbNodes() );
1125     for ( int iface = 0; iface < nbFaces; iface++ )
1126     {
1127       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1128       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129       bool toReverse = ( facetOri[ iface ] != neededOri );
1130
1131       quantities[ iface ] = nbFaceNodes;
1132
1133       if ( toReverse )
1134         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135           poly_nodes.push_back( nodes[ inode ]);
1136       else
1137         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1138     }
1139     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1140   }
1141   else // other elements
1142   {
1143     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145     if ( interlace.empty() )
1146     {
1147       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1148     }
1149     else
1150     {
1151       SMDS_MeshCell::applyInterlace( interlace, nodes );
1152     }
1153     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1154   }
1155   return false;
1156 }
1157
1158 //================================================================================
1159 /*!
1160  * \brief Reorient faces.
1161  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162  * \param theDirection - desired direction of normal of \a theFace
1163  * \param theFace - one of \a theFaces that should be oriented according to
1164  *        \a theDirection and whose orientation defines orientation of other faces
1165  * \return number of reoriented faces.
1166  */
1167 //================================================================================
1168
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1170                                   const gp_Dir&            theDirection,
1171                                   const SMDS_MeshElement * theFace)
1172 {
1173   int nbReori = 0;
1174   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1175
1176   if ( theFaces.empty() )
1177   {
1178     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179     while ( fIt->more() )
1180       theFaces.insert( theFaces.end(), fIt->next() );
1181   }
1182
1183   // orient theFace according to theDirection
1184   gp_XYZ normal;
1185   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186   if ( normal * theDirection.XYZ() < 0 )
1187     nbReori += Reorient( theFace );
1188
1189   // Orient other faces
1190
1191   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192   TIDSortedElemSet avoidSet;
1193   set< SMESH_TLink > checkedLinks;
1194   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1195
1196   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197     theFaces.erase( theFace );
1198   startFaces.insert( theFace );
1199
1200   int nodeInd1, nodeInd2;
1201   const SMDS_MeshElement*           otherFace;
1202   vector< const SMDS_MeshElement* > facesNearLink;
1203   vector< std::pair< int, int > >   nodeIndsOfFace;
1204
1205   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206   while ( !startFaces.empty() )
1207   {
1208     startFace = startFaces.begin();
1209     theFace = *startFace;
1210     startFaces.erase( startFace );
1211     if ( !visitedFaces.insert( theFace ).second )
1212       continue;
1213
1214     avoidSet.clear();
1215     avoidSet.insert(theFace);
1216
1217     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1218
1219     const int nbNodes = theFace->NbCornerNodes();
1220     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1221     {
1222       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223       linkIt_isNew = checkedLinks.insert( link );
1224       if ( !linkIt_isNew.second )
1225       {
1226         // link has already been checked and won't be encountered more
1227         // if the group (theFaces) is manifold
1228         //checkedLinks.erase( linkIt_isNew.first );
1229       }
1230       else
1231       {
1232         facesNearLink.clear();
1233         nodeIndsOfFace.clear();
1234         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1235                                                              theFaces, avoidSet,
1236                                                              &nodeInd1, &nodeInd2 )))
1237           if ( otherFace != theFace)
1238           {
1239             facesNearLink.push_back( otherFace );
1240             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241             avoidSet.insert( otherFace );
1242           }
1243         if ( facesNearLink.size() > 1 )
1244         {
1245           // NON-MANIFOLD mesh shell !
1246           // select a face most co-directed with theFace,
1247           // other faces won't be visited this time
1248           gp_XYZ NF, NOF;
1249           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250           double proj, maxProj = -1;
1251           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253             if (( proj = Abs( NF * NOF )) > maxProj ) {
1254               maxProj = proj;
1255               otherFace = facesNearLink[i];
1256               nodeInd1  = nodeIndsOfFace[i].first;
1257               nodeInd2  = nodeIndsOfFace[i].second;
1258             }
1259           }
1260           // not to visit rejected faces
1261           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263               visitedFaces.insert( facesNearLink[i] );
1264         }
1265         else if ( facesNearLink.size() == 1 )
1266         {
1267           otherFace = facesNearLink[0];
1268           nodeInd1  = nodeIndsOfFace.back().first;
1269           nodeInd2  = nodeIndsOfFace.back().second;
1270         }
1271         if ( otherFace && otherFace != theFace)
1272         {
1273           // link must be reverse in otherFace if orientation to otherFace
1274           // is same as that of theFace
1275           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1276           {
1277             nbReori += Reorient( otherFace );
1278           }
1279           startFaces.insert( otherFace );
1280         }
1281       }
1282       std::swap( link.first, link.second ); // reverse the link
1283     }
1284   }
1285   return nbReori;
1286 }
1287
1288 //================================================================================
1289 /*!
1290  * \brief Reorient faces basing on orientation of adjacent volumes.
1291  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292  * \param theVolumes - reference volumes.
1293  * \param theOutsideNormal - to orient faces to have their normal
1294  *        pointing either \a outside or \a inside the adjacent volumes.
1295  * \return number of reoriented faces.
1296  */
1297 //================================================================================
1298
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300                                       TIDSortedElemSet & theVolumes,
1301                                       const bool         theOutsideNormal)
1302 {
1303   int nbReori = 0;
1304
1305   SMDS_ElemIteratorPtr faceIt;
1306   if ( theFaces.empty() )
1307     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1308   else
1309     faceIt = SMESHUtils::elemSetIterator( theFaces );
1310
1311   vector< const SMDS_MeshNode* > faceNodes;
1312   TIDSortedElemSet checkedVolumes;
1313   set< const SMDS_MeshNode* > faceNodesSet;
1314   SMDS_VolumeTool volumeTool;
1315
1316   while ( faceIt->more() ) // loop on given faces
1317   {
1318     const SMDS_MeshElement* face = faceIt->next();
1319     if ( face->GetType() != SMDSAbs_Face )
1320       continue;
1321
1322     const size_t nbCornersNodes = face->NbCornerNodes();
1323     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1324
1325     checkedVolumes.clear();
1326     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327     while ( vIt->more() )
1328     {
1329       const SMDS_MeshElement* volume = vIt->next();
1330
1331       if ( !checkedVolumes.insert( volume ).second )
1332         continue;
1333       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1334         continue;
1335
1336       // is volume adjacent?
1337       bool allNodesCommon = true;
1338       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340       if ( !allNodesCommon )
1341         continue;
1342
1343       // get nodes of a corresponding volume facet
1344       faceNodesSet.clear();
1345       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346       volumeTool.Set( volume );
1347       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348       if ( facetID < 0 ) continue;
1349       volumeTool.SetExternalNormal();
1350       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1351
1352       // compare order of faceNodes and facetNodes
1353       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1354       int iNN[2];
1355       for ( int i = 0; i < 2; ++i )
1356       {
1357         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359           if ( faceNodes[ iN ] == n )
1360           {
1361             iNN[ i ] = iN;
1362             break;
1363           }
1364       }
1365       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366       if ( isOutside != theOutsideNormal )
1367         nbReori += Reorient( face );
1368     }
1369   }  // loop on given faces
1370
1371   return nbReori;
1372 }
1373
1374 //=======================================================================
1375 //function : getBadRate
1376 //purpose  :
1377 //=======================================================================
1378
1379 static double getBadRate (const SMDS_MeshElement*               theElem,
1380                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1381 {
1382   SMESH::Controls::TSequenceOfXYZ P;
1383   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1384     return 1e100;
1385   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1387 }
1388
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose  : Cut quadrangles into triangles.
1392 //           theCrit is used to select a diagonal to cut
1393 //=======================================================================
1394
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1396                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1397 {
1398   ClearLastCreated();
1399
1400   if ( !theCrit.get() )
1401     return false;
1402
1403   SMESHDS_Mesh *       aMesh = GetMeshDS();
1404   Handle(Geom_Surface) surface;
1405   SMESH_MesherHelper   helper( *GetMesh() );
1406
1407   myLastCreatedElems.reserve( theElems.size() * 2 );
1408
1409   TIDSortedElemSet::iterator itElem;
1410   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1411   {
1412     const SMDS_MeshElement* elem = *itElem;
1413     if ( !elem || elem->GetType() != SMDSAbs_Face )
1414       continue;
1415     if ( elem->NbCornerNodes() != 4 )
1416       continue;
1417
1418     // retrieve element nodes
1419     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1420
1421     // compare two sets of possible triangles
1422     double aBadRate1, aBadRate2; // to what extent a set is bad
1423     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1426
1427     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1430
1431     const int aShapeId = FindShape( elem );
1432     const SMDS_MeshElement* newElem1 = 0;
1433     const SMDS_MeshElement* newElem2 = 0;
1434
1435     if ( !elem->IsQuadratic() ) // split linear quadrangle
1436     {
1437       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439       if ( aBadRate1 <= aBadRate2 ) {
1440         // tr1 + tr2 is better
1441         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1443       }
1444       else {
1445         // tr3 + tr4 is better
1446         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1448       }
1449     }
1450     else // split quadratic quadrangle
1451     {
1452       helper.SetIsQuadratic( true );
1453       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1454
1455       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456       if ( aNodes.size() == 9 )
1457       {
1458         helper.SetIsBiQuadratic( true );
1459         if ( aBadRate1 <= aBadRate2 )
1460           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1461         else
1462           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1463       }
1464       // create a new element
1465       if ( aBadRate1 <= aBadRate2 ) {
1466         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1468       }
1469       else {
1470         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1472       }
1473     } // quadratic case
1474
1475     // care of a new element
1476
1477     myLastCreatedElems.push_back(newElem1);
1478     myLastCreatedElems.push_back(newElem2);
1479     AddToSameGroups( newElem1, elem, aMesh );
1480     AddToSameGroups( newElem2, elem, aMesh );
1481
1482     // put a new triangle on the same shape
1483     if ( aShapeId )
1484       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1486
1487     aMesh->RemoveElement( elem );
1488   }
1489   return true;
1490 }
1491
1492 //=======================================================================
1493 /*!
1494  * \brief Split each of given quadrangles into 4 triangles.
1495  * \param theElems - The faces to be split. If empty all faces are split.
1496  */
1497 //=======================================================================
1498
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1500 {
1501   ClearLastCreated();
1502   myLastCreatedElems.reserve( theElems.size() * 4 );
1503
1504   SMESH_MesherHelper helper( *GetMesh() );
1505   helper.SetElementsOnShape( true );
1506
1507   // get standalone groups of faces
1508   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1509   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1510     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1511       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1512         allFaceGroups.push_back( & group->SMDSGroup() );
1513
1514   bool   checkUV;
1515   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1516   gp_XYZ xyz[9];
1517   vector< const SMDS_MeshNode* > nodes;
1518   SMESHDS_SubMesh*               subMeshDS = 0;
1519   TopoDS_Face                    F;
1520   Handle(Geom_Surface)           surface;
1521   TopLoc_Location                loc;
1522
1523   SMDS_ElemIteratorPtr faceIt;
1524   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1525   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1526
1527   while ( faceIt->more() )
1528   {
1529     const SMDS_MeshElement* quad = faceIt->next();
1530     if ( !quad || quad->NbCornerNodes() != 4 )
1531       continue;
1532
1533     // get a surface the quad is on
1534
1535     if ( quad->getshapeId() < 1 )
1536     {
1537       F.Nullify();
1538       helper.SetSubShape( 0 );
1539       subMeshDS = 0;
1540     }
1541     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542     {
1543       helper.SetSubShape( quad->getshapeId() );
1544       if ( !helper.GetSubShape().IsNull() &&
1545            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546       {
1547         F = TopoDS::Face( helper.GetSubShape() );
1548         surface = BRep_Tool::Surface( F, loc );
1549         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1550       }
1551       else
1552       {
1553         helper.SetSubShape( 0 );
1554         subMeshDS = 0;
1555       }
1556     }
1557
1558     // create a central node
1559
1560     const SMDS_MeshNode* nCentral;
1561     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562
1563     if ( nodes.size() == 9 )
1564     {
1565       nCentral = nodes.back();
1566     }
1567     else
1568     {
1569       size_t iN = 0;
1570       if ( F.IsNull() )
1571       {
1572         for ( ; iN < nodes.size(); ++iN )
1573           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1574
1575         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577
1578         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579                                    xyz[0], xyz[1], xyz[2], xyz[3],
1580                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1581       }
1582       else
1583       {
1584         for ( ; iN < nodes.size(); ++iN )
1585           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586
1587         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589
1590         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591                                   uv[0], uv[1], uv[2], uv[3],
1592                                   uv[4], uv[5], uv[6], uv[7] );
1593
1594         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1595         xyz[ 8 ] = p.XYZ();
1596       }
1597
1598       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599                                  uv[8].X(), uv[8].Y() );
1600       myLastCreatedNodes.push_back( nCentral );
1601     }
1602
1603     helper.SetIsQuadratic  ( nodes.size() > 4 );
1604     helper.SetIsBiQuadratic( nodes.size() == 9 );
1605     if ( helper.GetIsQuadratic() )
1606       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1607
1608     // select groups to update
1609     faceGroups.clear();
1610     for ( SMDS_MeshGroup* group : allFaceGroups )
1611       if ( group->Remove( quad ))
1612         faceGroups.push_back( group );
1613
1614     // create 4 triangles
1615
1616     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1617
1618     for ( int i = 0; i < 4; ++i )
1619     {
1620       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1621                                                nodes[(i+1)%4],
1622                                                nCentral );
1623       myLastCreatedElems.push_back( tria );
1624       for ( SMDS_MeshGroup* group : faceGroups )
1625         group->Add( tria );
1626     }
1627   }
1628 }
1629
1630 //=======================================================================
1631 //function : BestSplit
1632 //purpose  : Find better diagonal for cutting.
1633 //=======================================================================
1634
1635 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1636                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1637 {
1638   ClearLastCreated();
1639
1640   if (!theCrit.get())
1641     return -1;
1642
1643   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1644     return -1;
1645
1646   if( theQuad->NbNodes()==4 ||
1647       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1648
1649     // retrieve element nodes
1650     const SMDS_MeshNode* aNodes [4];
1651     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1652     int i = 0;
1653     //while (itN->more())
1654     while (i<4) {
1655       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1656     }
1657     // compare two sets of possible triangles
1658     double aBadRate1, aBadRate2; // to what extent a set is bad
1659     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1660     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1661     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1662
1663     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1664     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1665     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1666     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1667     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1668     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1669       return 1; // diagonal 1-3
1670
1671     return 2; // diagonal 2-4
1672   }
1673   return -1;
1674 }
1675
1676 namespace
1677 {
1678   // Methods of splitting volumes into tetra
1679
1680   const int theHexTo5_1[5*4+1] =
1681     {
1682       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1683     };
1684   const int theHexTo5_2[5*4+1] =
1685     {
1686       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1687     };
1688   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1689
1690   const int theHexTo6_1[6*4+1] =
1691     {
1692       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
1693     };
1694   const int theHexTo6_2[6*4+1] =
1695     {
1696       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
1697     };
1698   const int theHexTo6_3[6*4+1] =
1699     {
1700       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
1701     };
1702   const int theHexTo6_4[6*4+1] =
1703     {
1704       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
1705     };
1706   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1707
1708   const int thePyraTo2_1[2*4+1] =
1709     {
1710       0, 1, 2, 4,    0, 2, 3, 4,   -1
1711     };
1712   const int thePyraTo2_2[2*4+1] =
1713     {
1714       1, 2, 3, 4,    1, 3, 0, 4,   -1
1715     };
1716   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1717
1718   const int thePentaTo3_1[3*4+1] =
1719     {
1720       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1721     };
1722   const int thePentaTo3_2[3*4+1] =
1723     {
1724       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1725     };
1726   const int thePentaTo3_3[3*4+1] =
1727     {
1728       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1729     };
1730   const int thePentaTo3_4[3*4+1] =
1731     {
1732       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1733     };
1734   const int thePentaTo3_5[3*4+1] =
1735     {
1736       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1737     };
1738   const int thePentaTo3_6[3*4+1] =
1739     {
1740       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1741     };
1742   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1743                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1744
1745   // Methods of splitting hexahedron into prisms
1746
1747   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1748     {
1749       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
1750     };
1751   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1752     {
1753       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
1754     };
1755   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1756     {
1757       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
1758     };
1759
1760   const int theHexTo2Prisms_BT_1[6*2+1] =
1761     {
1762       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1763     };
1764   const int theHexTo2Prisms_BT_2[6*2+1] =
1765     {
1766       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1767     };
1768   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1769
1770   const int theHexTo2Prisms_LR_1[6*2+1] =
1771     {
1772       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1773     };
1774   const int theHexTo2Prisms_LR_2[6*2+1] =
1775     {
1776       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1777     };
1778   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1779
1780   const int theHexTo2Prisms_FB_1[6*2+1] =
1781     {
1782       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1783     };
1784   const int theHexTo2Prisms_FB_2[6*2+1] =
1785     {
1786       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1787     };
1788   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1789
1790
1791   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1792   {
1793     int _n1, _n2, _n3;
1794     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1795     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1796     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1797                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1798   };
1799   struct TSplitMethod
1800   {
1801     int        _nbSplits;
1802     int        _nbCorners;
1803     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1804     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1805     bool       _ownConn;      //!< to delete _connectivity in destructor
1806     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1807
1808     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1809       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1810     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1811     bool hasFacet( const TTriangleFacet& facet ) const
1812     {
1813       if ( _nbCorners == 4 )
1814       {
1815         const int* tetConn = _connectivity;
1816         for ( ; tetConn[0] >= 0; tetConn += 4 )
1817           if (( facet.contains( tetConn[0] ) +
1818                 facet.contains( tetConn[1] ) +
1819                 facet.contains( tetConn[2] ) +
1820                 facet.contains( tetConn[3] )) == 3 )
1821             return true;
1822       }
1823       else // prism, _nbCorners == 6
1824       {
1825         const int* prismConn = _connectivity;
1826         for ( ; prismConn[0] >= 0; prismConn += 6 )
1827         {
1828           if (( facet.contains( prismConn[0] ) &&
1829                 facet.contains( prismConn[1] ) &&
1830                 facet.contains( prismConn[2] ))
1831               ||
1832               ( facet.contains( prismConn[3] ) &&
1833                 facet.contains( prismConn[4] ) &&
1834                 facet.contains( prismConn[5] )))
1835             return true;
1836         }
1837       }
1838       return false;
1839     }
1840   };
1841
1842   //=======================================================================
1843   /*!
1844    * \brief return TSplitMethod for the given element to split into tetrahedra
1845    */
1846   //=======================================================================
1847
1848   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1849   {
1850     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1851
1852     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1853     // an edge and a face barycenter; tertaherdons are based on triangles and
1854     // a volume barycenter
1855     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1856
1857     // Find out how adjacent volumes are split
1858
1859     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1860     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1861     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1862     {
1863       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1864       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1865       if ( nbNodes < 4 ) continue;
1866
1867       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1868       const int* nInd = vol.GetFaceNodesIndices( iF );
1869       if ( nbNodes == 4 )
1870       {
1871         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1872         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1873         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1874         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1875       }
1876       else
1877       {
1878         int iCom = 0; // common node of triangle faces to split into
1879         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1880         {
1881           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1882                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1883                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1884           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1885                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1886                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1887           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1888           {
1889             triaSplits.push_back( t012 );
1890             triaSplits.push_back( t023 );
1891             break;
1892           }
1893         }
1894       }
1895       if ( !triaSplits.empty() )
1896         hasAdjacentSplits = true;
1897     }
1898
1899     // Among variants of split method select one compliant with adjacent volumes
1900
1901     TSplitMethod method;
1902     if ( !vol.Element()->IsPoly() && !is24TetMode )
1903     {
1904       int nbVariants = 2, nbTet = 0;
1905       const int** connVariants = 0;
1906       switch ( vol.Element()->GetEntityType() )
1907       {
1908       case SMDSEntity_Hexa:
1909       case SMDSEntity_Quad_Hexa:
1910       case SMDSEntity_TriQuad_Hexa:
1911         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1912           connVariants = theHexTo5, nbTet = 5;
1913         else
1914           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1915         break;
1916       case SMDSEntity_Pyramid:
1917       case SMDSEntity_Quad_Pyramid:
1918         connVariants = thePyraTo2;  nbTet = 2;
1919         break;
1920       case SMDSEntity_Penta:
1921       case SMDSEntity_Quad_Penta:
1922       case SMDSEntity_BiQuad_Penta:
1923         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1924         break;
1925       default:
1926         nbVariants = 0;
1927       }
1928       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1929       {
1930         // check method compliance with adjacent tetras,
1931         // all found splits must be among facets of tetras described by this method
1932         method = TSplitMethod( nbTet, connVariants[variant] );
1933         if ( hasAdjacentSplits && method._nbSplits > 0 )
1934         {
1935           bool facetCreated = true;
1936           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1937           {
1938             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1939             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1940               facetCreated = method.hasFacet( *facet );
1941           }
1942           if ( !facetCreated )
1943             method = TSplitMethod(0); // incompatible method
1944         }
1945       }
1946     }
1947     if ( method._nbSplits < 1 )
1948     {
1949       // No standard method is applicable, use a generic solution:
1950       // each facet of a volume is split into triangles and
1951       // each of triangles and a volume barycenter form a tetrahedron.
1952
1953       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1954
1955       int* connectivity = new int[ maxTetConnSize + 1 ];
1956       method._connectivity = connectivity;
1957       method._ownConn = true;
1958       method._baryNode = !isHex27; // to create central node or not
1959
1960       int connSize = 0;
1961       int baryCenInd = vol.NbNodes() - int( isHex27 );
1962       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1963       {
1964         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1965         const int*   nInd = vol.GetFaceNodesIndices( iF );
1966         // find common node of triangle facets of tetra to create
1967         int iCommon = 0; // index in linear numeration
1968         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1969         if ( !triaSplits.empty() )
1970         {
1971           // by found facets
1972           const TTriangleFacet* facet = &triaSplits.front();
1973           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1974             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1975                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1976               break;
1977         }
1978         else if ( nbNodes > 3 && !is24TetMode )
1979         {
1980           // find the best method of splitting into triangles by aspect ratio
1981           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1982           map< double, int > badness2iCommon;
1983           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1984           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1985           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1986           {
1987             double badness = 0;
1988             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1989             {
1990               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1991                                       nodes[ iQ*((iLast-1)%nbNodes)],
1992                                       nodes[ iQ*((iLast  )%nbNodes)]);
1993               badness += getBadRate( &tria, aspectRatio );
1994             }
1995             badness2iCommon.insert( make_pair( badness, iCommon ));
1996           }
1997           // use iCommon with lowest badness
1998           iCommon = badness2iCommon.begin()->second;
1999         }
2000         if ( iCommon >= nbNodes )
2001           iCommon = 0; // something wrong
2002
2003         // fill connectivity of tetrahedra based on a current face
2004         int nbTet = nbNodes - 2;
2005         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2006         {
2007           int faceBaryCenInd;
2008           if ( isHex27 )
2009           {
2010             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2011             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2012           }
2013           else
2014           {
2015             method._faceBaryNode[ iF ] = 0;
2016             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2017           }
2018           nbTet = nbNodes;
2019           for ( int i = 0; i < nbTet; ++i )
2020           {
2021             int i1 = i, i2 = (i+1) % nbNodes;
2022             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2023             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2024             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2025             connectivity[ connSize++ ] = faceBaryCenInd;
2026             connectivity[ connSize++ ] = baryCenInd;
2027           }
2028         }
2029         else
2030         {
2031           for ( int i = 0; i < nbTet; ++i )
2032           {
2033             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2034             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2035             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2036             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038             connectivity[ connSize++ ] = baryCenInd;
2039           }
2040         }
2041         method._nbSplits += nbTet;
2042
2043       } // loop on volume faces
2044
2045       connectivity[ connSize++ ] = -1;
2046
2047     } // end of generic solution
2048
2049     return method;
2050   }
2051   //=======================================================================
2052   /*!
2053    * \brief return TSplitMethod to split haxhedron into prisms
2054    */
2055   //=======================================================================
2056
2057   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2058                                     const int        methodFlags,
2059                                     const int        facetToSplit)
2060   {
2061     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2062     // B, T, L, B, R, F
2063     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2064
2065     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2066     {
2067       static TSplitMethod to4methods[4]; // order BT, LR, FB
2068       if ( to4methods[iF]._nbSplits == 0 )
2069       {
2070         switch ( iF ) {
2071         case 0:
2072           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2073           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2074           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2075           break;
2076         case 1:
2077           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2078           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2079           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2080           break;
2081         case 2:
2082           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2083           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2084           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2085           break;
2086         default: return to4methods[3];
2087         }
2088         to4methods[iF]._nbSplits  = 4;
2089         to4methods[iF]._nbCorners = 6;
2090       }
2091       return to4methods[iF];
2092     }
2093     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2094
2095     TSplitMethod method;
2096
2097     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2098
2099     const int nbVariants = 2, nbSplits = 2;
2100     const int** connVariants = 0;
2101     switch ( iF ) {
2102     case 0: connVariants = theHexTo2Prisms_BT; break;
2103     case 1: connVariants = theHexTo2Prisms_LR; break;
2104     case 2: connVariants = theHexTo2Prisms_FB; break;
2105     default: return method;
2106     }
2107
2108     // look for prisms adjacent via facetToSplit and an opposite one
2109     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2110     {
2111       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2112       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2113       if ( nbNodes != 4 ) return method;
2114
2115       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2116       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2117       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2118       TTriangleFacet* t;
2119       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2120         t = &t012;
2121       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2122         t = &t123;
2123       else
2124         continue;
2125
2126       // there are adjacent prism
2127       for ( int variant = 0; variant < nbVariants; ++variant )
2128       {
2129         // check method compliance with adjacent prisms,
2130         // the found prism facets must be among facets of prisms described by current method
2131         method._nbSplits     = nbSplits;
2132         method._nbCorners    = 6;
2133         method._connectivity = connVariants[ variant ];
2134         if ( method.hasFacet( *t ))
2135           return method;
2136       }
2137     }
2138
2139     // No adjacent prisms. Select a variant with a best aspect ratio.
2140
2141     double badness[2] = { 0., 0. };
2142     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2143     const SMDS_MeshNode** nodes = vol.GetNodes();
2144     for ( int variant = 0; variant < nbVariants; ++variant )
2145       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2146       {
2147         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2148         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2149
2150         method._connectivity = connVariants[ variant ];
2151         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2152         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2153         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2154
2155         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2156                                 nodes[ t->_n2 ],
2157                                 nodes[ t->_n3 ] );
2158         badness[ variant ] += getBadRate( &tria, aspectRatio );
2159       }
2160     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2161
2162     method._nbSplits     = nbSplits;
2163     method._nbCorners    = 6;
2164     method._connectivity = connVariants[ iBetter ];
2165
2166     return method;
2167   }
2168
2169   //================================================================================
2170   /*!
2171    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2172    */
2173   //================================================================================
2174
2175   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2176                                        const SMDSAbs_GeometryType geom ) const
2177   {
2178     // find the tetrahedron including the three nodes of facet
2179     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2180     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2181     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2182     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2183     while ( volIt1->more() )
2184     {
2185       const SMDS_MeshElement* v = volIt1->next();
2186       if ( v->GetGeomType() != geom )
2187         continue;
2188       const int lastCornerInd = v->NbCornerNodes() - 1;
2189       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2190         continue; // medium node not allowed
2191       const int ind2 = v->GetNodeIndex( n2 );
2192       if ( ind2 < 0 || lastCornerInd < ind2 )
2193         continue;
2194       const int ind3 = v->GetNodeIndex( n3 );
2195       if ( ind3 < 0 || lastCornerInd < ind3 )
2196         continue;
2197       return true;
2198     }
2199     return false;
2200   }
2201
2202   //=======================================================================
2203   /*!
2204    * \brief A key of a face of volume
2205    */
2206   //=======================================================================
2207
2208   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2209   {
2210     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2211     {
2212       TIDSortedNodeSet sortedNodes;
2213       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2214       int nbNodes = vol.NbFaceNodes( iF );
2215       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2216       for ( int i = 0; i < nbNodes; i += iQ )
2217         sortedNodes.insert( fNodes[i] );
2218       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2219       first.first   = (*(n++))->GetID();
2220       first.second  = (*(n++))->GetID();
2221       second.first  = (*(n++))->GetID();
2222       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2223     }
2224   };
2225 } // namespace
2226
2227 //=======================================================================
2228 //function : SplitVolumes
2229 //purpose  : Split volume elements into tetrahedra or prisms.
2230 //           If facet ID < 0, element is split into tetrahedra,
2231 //           else a hexahedron is split into prisms so that the given facet is
2232 //           split into triangles
2233 //=======================================================================
2234
2235 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2236                                      const int            theMethodFlags)
2237 {
2238   SMDS_VolumeTool    volTool;
2239   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2240   fHelper.ToFixNodeParameters( true );
2241
2242   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2243   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2244
2245   SMESH_SequenceOfElemPtr newNodes, newElems;
2246
2247   // map face of volume to it's baricenrtic node
2248   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2249   double bc[3];
2250   vector<const SMDS_MeshElement* > splitVols;
2251
2252   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2253   for ( ; elem2facet != theElems.end(); ++elem2facet )
2254   {
2255     const SMDS_MeshElement* elem = elem2facet->first;
2256     const int       facetToSplit = elem2facet->second;
2257     if ( elem->GetType() != SMDSAbs_Volume )
2258       continue;
2259     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2260     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2261       continue;
2262
2263     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2264
2265     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2266                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2267                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2268     if ( splitMethod._nbSplits < 1 ) continue;
2269
2270     // find submesh to add new tetras to
2271     if ( !subMesh || !subMesh->Contains( elem ))
2272     {
2273       int shapeID = FindShape( elem );
2274       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2275       subMesh = GetMeshDS()->MeshElements( shapeID );
2276     }
2277     int iQ;
2278     if ( elem->IsQuadratic() )
2279     {
2280       iQ = 2;
2281       // add quadratic links to the helper
2282       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2283       {
2284         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2285         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2286         for ( int iN = 0; iN < nbN; iN += iQ )
2287           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2288       }
2289       helper.SetIsQuadratic( true );
2290     }
2291     else
2292     {
2293       iQ = 1;
2294       helper.SetIsQuadratic( false );
2295     }
2296     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2297                                         volTool.GetNodes() + elem->NbNodes() );
2298     helper.SetElementsOnShape( true );
2299     if ( splitMethod._baryNode )
2300     {
2301       // make a node at barycenter
2302       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2303       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2304       nodes.push_back( gcNode );
2305       newNodes.push_back( gcNode );
2306     }
2307     if ( !splitMethod._faceBaryNode.empty() )
2308     {
2309       // make or find baricentric nodes of faces
2310       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2311       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2312       {
2313         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2314           volFace2BaryNode.insert
2315           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2316         if ( !f_n->second )
2317         {
2318           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2319           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2320         }
2321         nodes.push_back( iF_n->second = f_n->second );
2322       }
2323     }
2324
2325     // make new volumes
2326     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2327     const int* volConn = splitMethod._connectivity;
2328     if ( splitMethod._nbCorners == 4 ) // tetra
2329       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331                                                                nodes[ volConn[1] ],
2332                                                                nodes[ volConn[2] ],
2333                                                                nodes[ volConn[3] ]));
2334     else // prisms
2335       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2336         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2337                                                                nodes[ volConn[1] ],
2338                                                                nodes[ volConn[2] ],
2339                                                                nodes[ volConn[3] ],
2340                                                                nodes[ volConn[4] ],
2341                                                                nodes[ volConn[5] ]));
2342
2343     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2344
2345     // Split faces on sides of the split volume
2346
2347     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2348     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2349     {
2350       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2351       if ( nbNodes < 4 ) continue;
2352
2353       // find an existing face
2354       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2355                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2356       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2357                                                                        /*noMedium=*/false))
2358       {
2359         // make triangles
2360         helper.SetElementsOnShape( false );
2361         vector< const SMDS_MeshElement* > triangles;
2362
2363         // find submesh to add new triangles in
2364         if ( !fSubMesh || !fSubMesh->Contains( face ))
2365         {
2366           int shapeID = FindShape( face );
2367           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2368         }
2369         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2370         if ( iF_n != splitMethod._faceBaryNode.end() )
2371         {
2372           const SMDS_MeshNode *baryNode = iF_n->second;
2373           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2374           {
2375             const SMDS_MeshNode* n1 = fNodes[iN];
2376             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2377             const SMDS_MeshNode *n3 = baryNode;
2378             if ( !volTool.IsFaceExternal( iF ))
2379               swap( n2, n3 );
2380             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2381           }
2382           if ( fSubMesh ) // update position of the bary node on geometry
2383           {
2384             if ( subMesh )
2385               subMesh->RemoveNode( baryNode );
2386             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2387             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2388             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2389             {
2390               fHelper.SetSubShape( s );
2391               gp_XY uv( 1e100, 1e100 );
2392               double distXYZ[4];
2393               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2394                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2395                    uv.X() < 1e100 )
2396               {
2397                 // node is too far from the surface
2398                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2399                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2400                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2401               }
2402             }
2403           }
2404         }
2405         else
2406         {
2407           // among possible triangles create ones described by split method
2408           const int* nInd = volTool.GetFaceNodesIndices( iF );
2409           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2410           int iCom = 0; // common node of triangle faces to split into
2411           list< TTriangleFacet > facets;
2412           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2413           {
2414             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2415                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2416                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2417             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2418                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2419                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2420             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2421             {
2422               facets.push_back( t012 );
2423               facets.push_back( t023 );
2424               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2425                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2426                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2427                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2428               break;
2429             }
2430           }
2431           list< TTriangleFacet >::iterator facet = facets.begin();
2432           if ( facet == facets.end() )
2433             break;
2434           for ( ; facet != facets.end(); ++facet )
2435           {
2436             if ( !volTool.IsFaceExternal( iF ))
2437               swap( facet->_n2, facet->_n3 );
2438             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2439                                                  volNodes[ facet->_n2 ],
2440                                                  volNodes[ facet->_n3 ]));
2441           }
2442         }
2443         for ( size_t i = 0; i < triangles.size(); ++i )
2444         {
2445           if ( !triangles[ i ]) continue;
2446           if ( fSubMesh )
2447             fSubMesh->AddElement( triangles[ i ]);
2448           newElems.push_back( triangles[ i ]);
2449         }
2450         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2451         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2452
2453       } // while a face based on facet nodes exists
2454     } // loop on volume faces to split them into triangles
2455
2456     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2457
2458     if ( geomType == SMDSEntity_TriQuad_Hexa )
2459     {
2460       // remove medium nodes that could become free
2461       for ( int i = 20; i < volTool.NbNodes(); ++i )
2462         if ( volNodes[i]->NbInverseElements() == 0 )
2463           GetMeshDS()->RemoveNode( volNodes[i] );
2464     }
2465   } // loop on volumes to split
2466
2467   myLastCreatedNodes = newNodes;
2468   myLastCreatedElems = newElems;
2469 }
2470
2471 //=======================================================================
2472 //function : GetHexaFacetsToSplit
2473 //purpose  : For hexahedra that will be split into prisms, finds facets to
2474 //           split into triangles. Only hexahedra adjacent to the one closest
2475 //           to theFacetNormal.Location() are returned.
2476 //param [in,out] theHexas - the hexahedra
2477 //param [in]     theFacetNormal - facet normal
2478 //param [out]    theFacets - the hexahedra and found facet IDs
2479 //=======================================================================
2480
2481 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2482                                              const gp_Ax1&     theFacetNormal,
2483                                              TFacetOfElem &    theFacets)
2484 {
2485 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2486
2487   // Find a hexa closest to the location of theFacetNormal
2488
2489   const SMDS_MeshElement* startHex;
2490   {
2491     // get SMDS_ElemIteratorPtr on theHexas
2492     typedef const SMDS_MeshElement*                                      TValue;
2493     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2494     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2495     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2496     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2497     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2498       ( new TElemSetIter( theHexas.begin(),
2499                           theHexas.end(),
2500                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2501
2502     SMESH_ElementSearcher* searcher =
2503       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2504
2505     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2506
2507     delete searcher;
2508
2509     if ( !startHex )
2510       throw SALOME_Exception( THIS_METHOD "startHex not found");
2511   }
2512
2513   // Select a facet of startHex by theFacetNormal
2514
2515   SMDS_VolumeTool vTool( startHex );
2516   double norm[3], dot, maxDot = 0;
2517   int facetID = -1;
2518   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2519     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2520     {
2521       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2522       if ( dot > maxDot )
2523       {
2524         facetID = iF;
2525         maxDot = dot;
2526       }
2527     }
2528   if ( facetID < 0 )
2529     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2530
2531   // Fill theFacets starting from facetID of startHex
2532
2533   // facets used for searching of volumes adjacent to already treated ones
2534   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2535   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2536   TFacetMap facetsToCheck;
2537
2538   set<const SMDS_MeshNode*> facetNodes;
2539   const SMDS_MeshElement*   curHex;
2540
2541   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2542
2543   while ( startHex )
2544   {
2545     // move in two directions from startHex via facetID
2546     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2547     {
2548       curHex       = startHex;
2549       int curFacet = facetID;
2550       if ( is2nd ) // do not treat startHex twice
2551       {
2552         vTool.Set( curHex );
2553         if ( vTool.IsFreeFace( curFacet, &curHex ))
2554         {
2555           curHex = 0;
2556         }
2557         else
2558         {
2559           vTool.GetFaceNodes( curFacet, facetNodes );
2560           vTool.Set( curHex );
2561           curFacet = vTool.GetFaceIndex( facetNodes );
2562         }
2563       }
2564       while ( curHex )
2565       {
2566         // store a facet to split
2567         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2568         {
2569           theFacets.insert( make_pair( curHex, -1 ));
2570           break;
2571         }
2572         if ( !allHex && !theHexas.count( curHex ))
2573           break;
2574
2575         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2576           theFacets.insert( make_pair( curHex, curFacet ));
2577         if ( !facetIt2isNew.second )
2578           break;
2579
2580         // remember not-to-split facets in facetsToCheck
2581         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2582         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2583         {
2584           if ( iF == curFacet && iF == oppFacet )
2585             continue;
2586           TVolumeFaceKey facetKey ( vTool, iF );
2587           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2588           pair< TFacetMap::iterator, bool > it2isnew =
2589             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2590           if ( !it2isnew.second )
2591             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2592         }
2593         // pass to a volume adjacent via oppFacet
2594         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2595         {
2596           curHex = 0;
2597         }
2598         else
2599         {
2600           // get a new curFacet
2601           vTool.GetFaceNodes( oppFacet, facetNodes );
2602           vTool.Set( curHex );
2603           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2604         }
2605       }
2606     } // move in two directions from startHex via facetID
2607
2608     // Find a new startHex by facetsToCheck
2609
2610     startHex = 0;
2611     facetID  = -1;
2612     TFacetMap::iterator fIt = facetsToCheck.begin();
2613     while ( !startHex && fIt != facetsToCheck.end() )
2614     {
2615       const TElemFacets&  elemFacets = fIt->second;
2616       const SMDS_MeshElement*    hex = elemFacets.first->first;
2617       int                 splitFacet = elemFacets.first->second;
2618       int               lateralFacet = elemFacets.second;
2619       facetsToCheck.erase( fIt );
2620       fIt = facetsToCheck.begin();
2621
2622       vTool.Set( hex );
2623       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2624            curHex->GetGeomType() != SMDSGeom_HEXA )
2625         continue;
2626       if ( !allHex && !theHexas.count( curHex ))
2627         continue;
2628
2629       startHex = curHex;
2630
2631       // find a facet of startHex to split
2632
2633       set<const SMDS_MeshNode*> lateralNodes;
2634       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2635       vTool.GetFaceNodes( splitFacet,   facetNodes );
2636       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2637       vTool.Set( startHex );
2638       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2639
2640       // look for a facet of startHex having common nodes with facetNodes
2641       // but not lateralFacet
2642       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2643       {
2644         if ( iF == lateralFacet )
2645           continue;
2646         int nbCommonNodes = 0;
2647         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2648         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2649           nbCommonNodes += facetNodes.count( nn[ iN ]);
2650
2651         if ( nbCommonNodes >= 2 )
2652         {
2653           facetID = iF;
2654           break;
2655         }
2656       }
2657       if ( facetID < 0 )
2658         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2659     }
2660   } //   while ( startHex )
2661
2662   return;
2663 }
2664
2665 namespace
2666 {
2667   //================================================================================
2668   /*!
2669    * \brief Selects nodes of several elements according to a given interlace
2670    *  \param [in] srcNodes - nodes to select from
2671    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2672    *  \param [in] interlace - indices of nodes for all elements
2673    *  \param [in] nbElems - nb of elements
2674    *  \param [in] nbNodes - nb of nodes in each element
2675    *  \param [in] mesh - the mesh
2676    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2677    *  \param [in] type - type of elements to look for
2678    */
2679   //================================================================================
2680
2681   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2682                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2683                     const int*                            interlace,
2684                     const int                             nbElems,
2685                     const int                             nbNodes,
2686                     SMESHDS_Mesh*                         mesh = 0,
2687                     list< const SMDS_MeshElement* >*      elemQueue=0,
2688                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2689   {
2690     for ( int iE = 0; iE < nbElems; ++iE )
2691     {
2692       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2693       const int*                         select = & interlace[iE*nbNodes];
2694       elemNodes.resize( nbNodes );
2695       for ( int iN = 0; iN < nbNodes; ++iN )
2696         elemNodes[iN] = srcNodes[ select[ iN ]];
2697     }
2698     const SMDS_MeshElement* e;
2699     if ( elemQueue )
2700       for ( int iE = 0; iE < nbElems; ++iE )
2701         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2702           elemQueue->push_back( e );
2703   }
2704 }
2705
2706 //=======================================================================
2707 /*
2708  * Split bi-quadratic elements into linear ones without creation of additional nodes
2709  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2710  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2711  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2712  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2713  *   will be split in order to keep the mesh conformal.
2714  *  \param elems - elements to split
2715  */
2716 //=======================================================================
2717
2718 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2719 {
2720   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2721   vector<const SMDS_MeshElement* > splitElems;
2722   list< const SMDS_MeshElement* > elemQueue;
2723   list< const SMDS_MeshElement* >::iterator elemIt;
2724
2725   SMESHDS_Mesh * mesh = GetMeshDS();
2726   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2727   int nbElems, nbNodes;
2728
2729   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2730   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2731   {
2732     elemQueue.clear();
2733     elemQueue.push_back( *elemSetIt );
2734     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2735     {
2736       const SMDS_MeshElement* elem = *elemIt;
2737       switch( elem->GetEntityType() )
2738       {
2739       case SMDSEntity_TriQuad_Hexa: // HEX27
2740       {
2741         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2742         nbElems  = nbNodes = 8;
2743         elemType = & hexaType;
2744
2745         // get nodes for new elements
2746         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2747                                  { 1,9,20,8,    17,22,26,21 },
2748                                  { 2,10,20,9,   18,23,26,22 },
2749                                  { 3,11,20,10,  19,24,26,23 },
2750                                  { 16,21,26,24, 4,12,25,15  },
2751                                  { 17,22,26,21, 5,13,25,12  },
2752                                  { 18,23,26,22, 6,14,25,13  },
2753                                  { 19,24,26,23, 7,15,25,14  }};
2754         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2755
2756         // add boundary faces to elemQueue
2757         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2758                                  { 4,5,6,7, 12,13,14,15, 25 },
2759                                  { 0,1,5,4, 8,17,12,16,  21 },
2760                                  { 1,2,6,5, 9,18,13,17,  22 },
2761                                  { 2,3,7,6, 10,19,14,18, 23 },
2762                                  { 3,0,4,7, 11,16,15,19, 24 }};
2763         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2764
2765         // add boundary segments to elemQueue
2766         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2767                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2768                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2769         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2770         break;
2771       }
2772       case SMDSEntity_BiQuad_Triangle: // TRIA7
2773       {
2774         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2775         nbElems = 3;
2776         nbNodes = 4;
2777         elemType = & quadType;
2778
2779         // get nodes for new elements
2780         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2781         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2782
2783         // add boundary segments to elemQueue
2784         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2785         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2786         break;
2787       }
2788       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2789       {
2790         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2791         nbElems = 4;
2792         nbNodes = 4;
2793         elemType = & quadType;
2794
2795         // get nodes for new elements
2796         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2797         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2798
2799         // add boundary segments to elemQueue
2800         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2801         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2802         break;
2803       }
2804       case SMDSEntity_Quad_Edge:
2805       {
2806         if ( elemIt == elemQueue.begin() )
2807           continue; // an elem is in theElems
2808         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2809         nbElems = 2;
2810         nbNodes = 2;
2811         elemType = & segType;
2812
2813         // get nodes for new elements
2814         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2815         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2816         break;
2817       }
2818       default: continue;
2819       } // switch( elem->GetEntityType() )
2820
2821       // Create new elements
2822
2823       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2824
2825       splitElems.clear();
2826
2827       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2828       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2829       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2830       //elemType->SetID( -1 );
2831
2832       for ( int iE = 0; iE < nbElems; ++iE )
2833         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2834
2835
2836       ReplaceElemInGroups( elem, splitElems, mesh );
2837
2838       if ( subMesh )
2839         for ( size_t i = 0; i < splitElems.size(); ++i )
2840           subMesh->AddElement( splitElems[i] );
2841     }
2842   }
2843 }
2844
2845 //=======================================================================
2846 //function : AddToSameGroups
2847 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2848 //=======================================================================
2849
2850 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2851                                         const SMDS_MeshElement* elemInGroups,
2852                                         SMESHDS_Mesh *          aMesh)
2853 {
2854   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2855   if (!groups.empty()) {
2856     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2857     for ( ; grIt != groups.end(); grIt++ ) {
2858       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2859       if ( group && group->Contains( elemInGroups ))
2860         group->SMDSGroup().Add( elemToAdd );
2861     }
2862   }
2863 }
2864
2865
2866 //=======================================================================
2867 //function : RemoveElemFromGroups
2868 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2869 //=======================================================================
2870 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2871                                              SMESHDS_Mesh *          aMesh)
2872 {
2873   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874   if (!groups.empty())
2875   {
2876     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2877     for (; GrIt != groups.end(); GrIt++)
2878     {
2879       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2880       if (!grp || grp->IsEmpty()) continue;
2881       grp->SMDSGroup().Remove(removeelem);
2882     }
2883   }
2884 }
2885
2886 //================================================================================
2887 /*!
2888  * \brief Replace elemToRm by elemToAdd in the all groups
2889  */
2890 //================================================================================
2891
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2893                                             const SMDS_MeshElement* elemToAdd,
2894                                             SMESHDS_Mesh *          aMesh)
2895 {
2896   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897   if (!groups.empty()) {
2898     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2899     for ( ; grIt != groups.end(); grIt++ ) {
2900       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2901       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2902         group->SMDSGroup().Add( elemToAdd );
2903     }
2904   }
2905 }
2906
2907 //================================================================================
2908 /*!
2909  * \brief Replace elemToRm by elemToAdd in the all groups
2910  */
2911 //================================================================================
2912
2913 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2914                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2915                                             SMESHDS_Mesh *                         aMesh)
2916 {
2917   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918   if (!groups.empty())
2919   {
2920     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2921     for ( ; grIt != groups.end(); grIt++ ) {
2922       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2923       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2924         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2925           group->SMDSGroup().Add( elemToAdd[ i ] );
2926     }
2927   }
2928 }
2929
2930 //=======================================================================
2931 //function : QuadToTri
2932 //purpose  : Cut quadrangles into triangles.
2933 //           theCrit is used to select a diagonal to cut
2934 //=======================================================================
2935
2936 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2937                                   const bool         the13Diag)
2938 {
2939   ClearLastCreated();
2940   myLastCreatedElems.reserve( theElems.size() * 2 );
2941
2942   SMESHDS_Mesh *       aMesh = GetMeshDS();
2943   Handle(Geom_Surface) surface;
2944   SMESH_MesherHelper   helper( *GetMesh() );
2945
2946   TIDSortedElemSet::iterator itElem;
2947   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2948   {
2949     const SMDS_MeshElement* elem = *itElem;
2950     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2951       continue;
2952
2953     if ( elem->NbNodes() == 4 ) {
2954       // retrieve element nodes
2955       const SMDS_MeshNode* aNodes [4];
2956       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2957       int i = 0;
2958       while ( itN->more() )
2959         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2960
2961       int aShapeId = FindShape( elem );
2962       const SMDS_MeshElement* newElem1 = 0;
2963       const SMDS_MeshElement* newElem2 = 0;
2964       if ( the13Diag ) {
2965         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2967       }
2968       else {
2969         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2970         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2971       }
2972       myLastCreatedElems.push_back(newElem1);
2973       myLastCreatedElems.push_back(newElem2);
2974       // put a new triangle on the same shape and add to the same groups
2975       if ( aShapeId )
2976       {
2977         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2978         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2979       }
2980       AddToSameGroups( newElem1, elem, aMesh );
2981       AddToSameGroups( newElem2, elem, aMesh );
2982       aMesh->RemoveElement( elem );
2983     }
2984
2985     // Quadratic quadrangle
2986
2987     else if ( elem->NbNodes() >= 8 )
2988     {
2989       // get surface elem is on
2990       int aShapeId = FindShape( elem );
2991       if ( aShapeId != helper.GetSubShapeID() ) {
2992         surface.Nullify();
2993         TopoDS_Shape shape;
2994         if ( aShapeId > 0 )
2995           shape = aMesh->IndexToShape( aShapeId );
2996         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2997           TopoDS_Face face = TopoDS::Face( shape );
2998           surface = BRep_Tool::Surface( face );
2999           if ( !surface.IsNull() )
3000             helper.SetSubShape( shape );
3001         }
3002       }
3003
3004       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3005       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3006       for ( int i = 0; itN->more(); ++i )
3007         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3008
3009       const SMDS_MeshNode* centrNode = aNodes[8];
3010       if ( centrNode == 0 )
3011       {
3012         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3013                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3014                                            surface.IsNull() );
3015         myLastCreatedNodes.push_back(centrNode);
3016       }
3017
3018       // create a new element
3019       const SMDS_MeshElement* newElem1 = 0;
3020       const SMDS_MeshElement* newElem2 = 0;
3021       if ( the13Diag ) {
3022         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3023                                   aNodes[6], aNodes[7], centrNode );
3024         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3025                                   centrNode, aNodes[4], aNodes[5] );
3026       }
3027       else {
3028         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3029                                   aNodes[7], aNodes[4], centrNode );
3030         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3031                                   centrNode, aNodes[5], aNodes[6] );
3032       }
3033       myLastCreatedElems.push_back(newElem1);
3034       myLastCreatedElems.push_back(newElem2);
3035       // put a new triangle on the same shape and add to the same groups
3036       if ( aShapeId )
3037       {
3038         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3039         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3040       }
3041       AddToSameGroups( newElem1, elem, aMesh );
3042       AddToSameGroups( newElem2, elem, aMesh );
3043       aMesh->RemoveElement( elem );
3044     }
3045   }
3046
3047   return true;
3048 }
3049
3050 //=======================================================================
3051 //function : getAngle
3052 //purpose  :
3053 //=======================================================================
3054
3055 double getAngle(const SMDS_MeshElement * tr1,
3056                 const SMDS_MeshElement * tr2,
3057                 const SMDS_MeshNode *    n1,
3058                 const SMDS_MeshNode *    n2)
3059 {
3060   double angle = 2. * M_PI; // bad angle
3061
3062   // get normals
3063   SMESH::Controls::TSequenceOfXYZ P1, P2;
3064   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3065        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3066     return angle;
3067   gp_Vec N1,N2;
3068   if(!tr1->IsQuadratic())
3069     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3070   else
3071     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3072   if ( N1.SquareMagnitude() <= gp::Resolution() )
3073     return angle;
3074   if(!tr2->IsQuadratic())
3075     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3076   else
3077     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3078   if ( N2.SquareMagnitude() <= gp::Resolution() )
3079     return angle;
3080
3081   // find the first diagonal node n1 in the triangles:
3082   // take in account a diagonal link orientation
3083   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3084   for ( int t = 0; t < 2; t++ ) {
3085     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3086     int i = 0, iDiag = -1;
3087     while ( it->more()) {
3088       const SMDS_MeshElement *n = it->next();
3089       if ( n == n1 || n == n2 ) {
3090         if ( iDiag < 0)
3091           iDiag = i;
3092         else {
3093           if ( i - iDiag == 1 )
3094             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3095           else
3096             nFirst[ t ] = n;
3097           break;
3098         }
3099       }
3100       i++;
3101     }
3102   }
3103   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3104     N2.Reverse();
3105
3106   angle = N1.Angle( N2 );
3107   //SCRUTE( angle );
3108   return angle;
3109 }
3110
3111 // =================================================
3112 // class generating a unique ID for a pair of nodes
3113 // and able to return nodes by that ID
3114 // =================================================
3115 class LinkID_Gen {
3116 public:
3117
3118   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3119     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3120   {}
3121
3122   long GetLinkID (const SMDS_MeshNode * n1,
3123                   const SMDS_MeshNode * n2) const
3124   {
3125     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3126   }
3127
3128   bool GetNodes (const long             theLinkID,
3129                  const SMDS_MeshNode* & theNode1,
3130                  const SMDS_MeshNode* & theNode2) const
3131   {
3132     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3133     if ( !theNode1 ) return false;
3134     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3135     if ( !theNode2 ) return false;
3136     return true;
3137   }
3138
3139 private:
3140   LinkID_Gen();
3141   const SMESHDS_Mesh* myMesh;
3142   long                myMaxID;
3143 };
3144
3145
3146 //=======================================================================
3147 //function : TriToQuad
3148 //purpose  : Fuse neighbour triangles into quadrangles.
3149 //           theCrit is used to select a neighbour to fuse with.
3150 //           theMaxAngle is a max angle between element normals at which
3151 //           fusion is still performed.
3152 //=======================================================================
3153
3154 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3155                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3156                                   const double                         theMaxAngle)
3157 {
3158   ClearLastCreated();
3159   myLastCreatedElems.reserve( theElems.size() / 2 );
3160
3161   if ( !theCrit.get() )
3162     return false;
3163
3164   SMESHDS_Mesh * aMesh = GetMeshDS();
3165
3166   // Prepare data for algo: build
3167   // 1. map of elements with their linkIDs
3168   // 2. map of linkIDs with their elements
3169
3170   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3171   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3172   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3173   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3174
3175   TIDSortedElemSet::iterator itElem;
3176   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3177   {
3178     const SMDS_MeshElement* elem = *itElem;
3179     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3180     bool IsTria = ( elem->NbCornerNodes()==3 );
3181     if (!IsTria) continue;
3182
3183     // retrieve element nodes
3184     const SMDS_MeshNode* aNodes [4];
3185     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3186     int i = 0;
3187     while ( i < 3 )
3188       aNodes[ i++ ] = itN->next();
3189     aNodes[ 3 ] = aNodes[ 0 ];
3190
3191     // fill maps
3192     for ( i = 0; i < 3; i++ ) {
3193       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3194       // check if elements sharing a link can be fused
3195       itLE = mapLi_listEl.find( link );
3196       if ( itLE != mapLi_listEl.end() ) {
3197         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3198           continue;
3199         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3200         //if ( FindShape( elem ) != FindShape( elem2 ))
3201         //  continue; // do not fuse triangles laying on different shapes
3202         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3203           continue; // avoid making badly shaped quads
3204         (*itLE).second.push_back( elem );
3205       }
3206       else {
3207         mapLi_listEl[ link ].push_back( elem );
3208       }
3209       mapEl_setLi [ elem ].insert( link );
3210     }
3211   }
3212   // Clean the maps from the links shared by a sole element, ie
3213   // links to which only one element is bound in mapLi_listEl
3214
3215   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3216     int nbElems = (*itLE).second.size();
3217     if ( nbElems < 2  ) {
3218       const SMDS_MeshElement* elem = (*itLE).second.front();
3219       SMESH_TLink link = (*itLE).first;
3220       mapEl_setLi[ elem ].erase( link );
3221       if ( mapEl_setLi[ elem ].empty() )
3222         mapEl_setLi.erase( elem );
3223     }
3224   }
3225
3226   // Algo: fuse triangles into quadrangles
3227
3228   while ( ! mapEl_setLi.empty() ) {
3229     // Look for the start element:
3230     // the element having the least nb of shared links
3231     const SMDS_MeshElement* startElem = 0;
3232     int minNbLinks = 4;
3233     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3234       int nbLinks = (*itEL).second.size();
3235       if ( nbLinks < minNbLinks ) {
3236         startElem = (*itEL).first;
3237         minNbLinks = nbLinks;
3238         if ( minNbLinks == 1 )
3239           break;
3240       }
3241     }
3242
3243     // search elements to fuse starting from startElem or links of elements
3244     // fused earlyer - startLinks
3245     list< SMESH_TLink > startLinks;
3246     while ( startElem || !startLinks.empty() ) {
3247       while ( !startElem && !startLinks.empty() ) {
3248         // Get an element to start, by a link
3249         SMESH_TLink linkId = startLinks.front();
3250         startLinks.pop_front();
3251         itLE = mapLi_listEl.find( linkId );
3252         if ( itLE != mapLi_listEl.end() ) {
3253           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3254           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3255           for ( ; itE != listElem.end() ; itE++ )
3256             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3257               startElem = (*itE);
3258           mapLi_listEl.erase( itLE );
3259         }
3260       }
3261
3262       if ( startElem ) {
3263         // Get candidates to be fused
3264         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3265         const SMESH_TLink *link12 = 0, *link13 = 0;
3266         startElem = 0;
3267         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3268         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3269         ASSERT( !setLi.empty() );
3270         set< SMESH_TLink >::iterator itLi;
3271         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3272         {
3273           const SMESH_TLink & link = (*itLi);
3274           itLE = mapLi_listEl.find( link );
3275           if ( itLE == mapLi_listEl.end() )
3276             continue;
3277
3278           const SMDS_MeshElement* elem = (*itLE).second.front();
3279           if ( elem == tr1 )
3280             elem = (*itLE).second.back();
3281           mapLi_listEl.erase( itLE );
3282           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3283             continue;
3284           if ( tr2 ) {
3285             tr3 = elem;
3286             link13 = &link;
3287           }
3288           else {
3289             tr2 = elem;
3290             link12 = &link;
3291           }
3292
3293           // add other links of elem to list of links to re-start from
3294           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3295           set< SMESH_TLink >::iterator it;
3296           for ( it = links.begin(); it != links.end(); it++ ) {
3297             const SMESH_TLink& link2 = (*it);
3298             if ( link2 != link )
3299               startLinks.push_back( link2 );
3300           }
3301         }
3302
3303         // Get nodes of possible quadrangles
3304         const SMDS_MeshNode *n12 [4], *n13 [4];
3305         bool Ok12 = false, Ok13 = false;
3306         const SMDS_MeshNode *linkNode1, *linkNode2;
3307         if(tr2) {
3308           linkNode1 = link12->first;
3309           linkNode2 = link12->second;
3310           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3311             Ok12 = true;
3312         }
3313         if(tr3) {
3314           linkNode1 = link13->first;
3315           linkNode2 = link13->second;
3316           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3317             Ok13 = true;
3318         }
3319
3320         // Choose a pair to fuse
3321         if ( Ok12 && Ok13 ) {
3322           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3323           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3324           double aBadRate12 = getBadRate( &quad12, theCrit );
3325           double aBadRate13 = getBadRate( &quad13, theCrit );
3326           if (  aBadRate13 < aBadRate12 )
3327             Ok12 = false;
3328           else
3329             Ok13 = false;
3330         }
3331
3332         // Make quadrangles
3333         // and remove fused elems and remove links from the maps
3334         mapEl_setLi.erase( tr1 );
3335         if ( Ok12 )
3336         {
3337           mapEl_setLi.erase( tr2 );
3338           mapLi_listEl.erase( *link12 );
3339           if ( tr1->NbNodes() == 3 )
3340           {
3341             const SMDS_MeshElement* newElem = 0;
3342             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3343             myLastCreatedElems.push_back(newElem);
3344             AddToSameGroups( newElem, tr1, aMesh );
3345             int aShapeId = tr1->getshapeId();
3346             if ( aShapeId )
3347               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3348             aMesh->RemoveElement( tr1 );
3349             aMesh->RemoveElement( tr2 );
3350           }
3351           else {
3352             vector< const SMDS_MeshNode* > N1;
3353             vector< const SMDS_MeshNode* > N2;
3354             getNodesFromTwoTria(tr1,tr2,N1,N2);
3355             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3356             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3357             // i.e. first nodes from both arrays form a new diagonal
3358             const SMDS_MeshNode* aNodes[8];
3359             aNodes[0] = N1[0];
3360             aNodes[1] = N1[1];
3361             aNodes[2] = N2[0];
3362             aNodes[3] = N2[1];
3363             aNodes[4] = N1[3];
3364             aNodes[5] = N2[5];
3365             aNodes[6] = N2[3];
3366             aNodes[7] = N1[5];
3367             const SMDS_MeshElement* newElem = 0;
3368             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3369               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3371             else
3372               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3373                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3374             myLastCreatedElems.push_back(newElem);
3375             AddToSameGroups( newElem, tr1, aMesh );
3376             int aShapeId = tr1->getshapeId();
3377             if ( aShapeId )
3378               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379             aMesh->RemoveElement( tr1 );
3380             aMesh->RemoveElement( tr2 );
3381             // remove middle node (9)
3382             if ( N1[4]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N1[4] );
3384             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3385               aMesh->RemoveNode( N1[6] );
3386             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3387               aMesh->RemoveNode( N2[6] );
3388           }
3389         }
3390         else if ( Ok13 )
3391         {
3392           mapEl_setLi.erase( tr3 );
3393           mapLi_listEl.erase( *link13 );
3394           if ( tr1->NbNodes() == 3 ) {
3395             const SMDS_MeshElement* newElem = 0;
3396             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3397             myLastCreatedElems.push_back(newElem);
3398             AddToSameGroups( newElem, tr1, aMesh );
3399             int aShapeId = tr1->getshapeId();
3400             if ( aShapeId )
3401               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3402             aMesh->RemoveElement( tr1 );
3403             aMesh->RemoveElement( tr3 );
3404           }
3405           else {
3406             vector< const SMDS_MeshNode* > N1;
3407             vector< const SMDS_MeshNode* > N2;
3408             getNodesFromTwoTria(tr1,tr3,N1,N2);
3409             // now we receive following N1 and N2 (using numeration as above image)
3410             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3411             // i.e. first nodes from both arrays form a new diagonal
3412             const SMDS_MeshNode* aNodes[8];
3413             aNodes[0] = N1[0];
3414             aNodes[1] = N1[1];
3415             aNodes[2] = N2[0];
3416             aNodes[3] = N2[1];
3417             aNodes[4] = N1[3];
3418             aNodes[5] = N2[5];
3419             aNodes[6] = N2[3];
3420             aNodes[7] = N1[5];
3421             const SMDS_MeshElement* newElem = 0;
3422             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3423               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3425             else
3426               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3427                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3428             myLastCreatedElems.push_back(newElem);
3429             AddToSameGroups( newElem, tr1, aMesh );
3430             int aShapeId = tr1->getshapeId();
3431             if ( aShapeId )
3432               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3433             aMesh->RemoveElement( tr1 );
3434             aMesh->RemoveElement( tr3 );
3435             // remove middle node (9)
3436             if ( N1[4]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N1[4] );
3438             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3439               aMesh->RemoveNode( N1[6] );
3440             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3441               aMesh->RemoveNode( N2[6] );
3442           }
3443         }
3444
3445         // Next element to fuse: the rejected one
3446         if ( tr3 )
3447           startElem = Ok12 ? tr3 : tr2;
3448
3449       } // if ( startElem )
3450     } // while ( startElem || !startLinks.empty() )
3451   } // while ( ! mapEl_setLi.empty() )
3452
3453   return true;
3454 }
3455
3456 //================================================================================
3457 /*!
3458  * \brief Return nodes linked to the given one
3459  * \param theNode - the node
3460  * \param linkedNodes - the found nodes
3461  * \param type - the type of elements to check
3462  *
3463  * Medium nodes are ignored
3464  */
3465 //================================================================================
3466
3467 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3468                                        TIDSortedElemSet &   linkedNodes,
3469                                        SMDSAbs_ElementType  type )
3470 {
3471   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3472   while ( elemIt->more() )
3473   {
3474     const SMDS_MeshElement* elem = elemIt->next();
3475     if(elem->GetType() == SMDSAbs_0DElement)
3476       continue;
3477
3478     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3479     if ( elem->GetType() == SMDSAbs_Volume )
3480     {
3481       SMDS_VolumeTool vol( elem );
3482       while ( nodeIt->more() ) {
3483         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3484         if ( theNode != n && vol.IsLinked( theNode, n ))
3485           linkedNodes.insert( n );
3486       }
3487     }
3488     else
3489     {
3490       for ( int i = 0; nodeIt->more(); ++i ) {
3491         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3492         if ( n == theNode ) {
3493           int iBefore = i - 1;
3494           int iAfter  = i + 1;
3495           if ( elem->IsQuadratic() ) {
3496             int nb = elem->NbNodes() / 2;
3497             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3498             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3499           }
3500           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3501           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3502         }
3503       }
3504     }
3505   }
3506 }
3507
3508 //=======================================================================
3509 //function : laplacianSmooth
3510 //purpose  : pulls theNode toward the center of surrounding nodes directly
3511 //           connected to that node along an element edge
3512 //=======================================================================
3513
3514 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3515                      const Handle(Geom_Surface)&          theSurface,
3516                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3517 {
3518   // find surrounding nodes
3519
3520   TIDSortedElemSet nodeSet;
3521   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3522
3523   // compute new coodrs
3524
3525   double coord[] = { 0., 0., 0. };
3526   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3527   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3528     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3529     if ( theSurface.IsNull() ) { // smooth in 3D
3530       coord[0] += node->X();
3531       coord[1] += node->Y();
3532       coord[2] += node->Z();
3533     }
3534     else { // smooth in 2D
3535       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3536       gp_XY* uv = theUVMap[ node ];
3537       coord[0] += uv->X();
3538       coord[1] += uv->Y();
3539     }
3540   }
3541   int nbNodes = nodeSet.size();
3542   if ( !nbNodes )
3543     return;
3544   coord[0] /= nbNodes;
3545   coord[1] /= nbNodes;
3546
3547   if ( !theSurface.IsNull() ) {
3548     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3549     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3550     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3551     coord[0] = p3d.X();
3552     coord[1] = p3d.Y();
3553     coord[2] = p3d.Z();
3554   }
3555   else
3556     coord[2] /= nbNodes;
3557
3558   // move node
3559
3560   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3561 }
3562
3563 //=======================================================================
3564 //function : centroidalSmooth
3565 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3566 //           surrounding elements
3567 //=======================================================================
3568
3569 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3570                       const Handle(Geom_Surface)&          theSurface,
3571                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3572 {
3573   gp_XYZ aNewXYZ(0.,0.,0.);
3574   SMESH::Controls::Area anAreaFunc;
3575   double totalArea = 0.;
3576   int nbElems = 0;
3577
3578   // compute new XYZ
3579
3580   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3581   while ( elemIt->more() )
3582   {
3583     const SMDS_MeshElement* elem = elemIt->next();
3584     nbElems++;
3585
3586     gp_XYZ elemCenter(0.,0.,0.);
3587     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3588     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3589     int nn = elem->NbNodes();
3590     if(elem->IsQuadratic()) nn = nn/2;
3591     int i=0;
3592     //while ( itN->more() ) {
3593     while ( i<nn ) {
3594       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3595       i++;
3596       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3597       aNodePoints.push_back( aP );
3598       if ( !theSurface.IsNull() ) { // smooth in 2D
3599         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3600         gp_XY* uv = theUVMap[ aNode ];
3601         aP.SetCoord( uv->X(), uv->Y(), 0. );
3602       }
3603       elemCenter += aP;
3604     }
3605     double elemArea = anAreaFunc.GetValue( aNodePoints );
3606     totalArea += elemArea;
3607     elemCenter /= nn;
3608     aNewXYZ += elemCenter * elemArea;
3609   }
3610   aNewXYZ /= totalArea;
3611   if ( !theSurface.IsNull() ) {
3612     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3613     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3614   }
3615
3616   // move node
3617
3618   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3619 }
3620
3621 //=======================================================================
3622 //function : getClosestUV
3623 //purpose  : return UV of closest projection
3624 //=======================================================================
3625
3626 static bool getClosestUV (Extrema_GenExtPS& projector,
3627                           const gp_Pnt&     point,
3628                           gp_XY &           result)
3629 {
3630   projector.Perform( point );
3631   if ( projector.IsDone() ) {
3632     double u = 0, v = 0, minVal = DBL_MAX;
3633     for ( int i = projector.NbExt(); i > 0; i-- )
3634       if ( projector.SquareDistance( i ) < minVal ) {
3635         minVal = projector.SquareDistance( i );
3636         projector.Point( i ).Parameter( u, v );
3637       }
3638     result.SetCoord( u, v );
3639     return true;
3640   }
3641   return false;
3642 }
3643
3644 //=======================================================================
3645 //function : Smooth
3646 //purpose  : Smooth theElements during theNbIterations or until a worst
3647 //           element has aspect ratio <= theTgtAspectRatio.
3648 //           Aspect Ratio varies in range [1.0, inf].
3649 //           If theElements is empty, the whole mesh is smoothed.
3650 //           theFixedNodes contains additionally fixed nodes. Nodes built
3651 //           on edges and boundary nodes are always fixed.
3652 //=======================================================================
3653
3654 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3655                                set<const SMDS_MeshNode*> & theFixedNodes,
3656                                const SmoothMethod          theSmoothMethod,
3657                                const int                   theNbIterations,
3658                                double                      theTgtAspectRatio,
3659                                const bool                  the2D)
3660 {
3661   ClearLastCreated();
3662
3663   if ( theTgtAspectRatio < 1.0 )
3664     theTgtAspectRatio = 1.0;
3665
3666   const double disttol = 1.e-16;
3667
3668   SMESH::Controls::AspectRatio aQualityFunc;
3669
3670   SMESHDS_Mesh* aMesh = GetMeshDS();
3671
3672   if ( theElems.empty() ) {
3673     // add all faces to theElems
3674     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3675     while ( fIt->more() ) {
3676       const SMDS_MeshElement* face = fIt->next();
3677       theElems.insert( theElems.end(), face );
3678     }
3679   }
3680   // get all face ids theElems are on
3681   set< int > faceIdSet;
3682   TIDSortedElemSet::iterator itElem;
3683   if ( the2D )
3684     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3685       int fId = FindShape( *itElem );
3686       // check that corresponding submesh exists and a shape is face
3687       if (fId &&
3688           faceIdSet.find( fId ) == faceIdSet.end() &&
3689           aMesh->MeshElements( fId )) {
3690         TopoDS_Shape F = aMesh->IndexToShape( fId );
3691         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3692           faceIdSet.insert( fId );
3693       }
3694     }
3695   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3696
3697   // ===============================================
3698   // smooth elements on each TopoDS_Face separately
3699   // ===============================================
3700
3701   SMESH_MesherHelper helper( *GetMesh() );
3702
3703   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3704   for ( ; fId != faceIdSet.rend(); ++fId )
3705   {
3706     // get face surface and submesh
3707     Handle(Geom_Surface) surface;
3708     SMESHDS_SubMesh* faceSubMesh = 0;
3709     TopoDS_Face face;
3710     double fToler2 = 0;
3711     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3712     bool isUPeriodic = false, isVPeriodic = false;
3713     if ( *fId )
3714     {
3715       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3716       surface = BRep_Tool::Surface( face );
3717       faceSubMesh = aMesh->MeshElements( *fId );
3718       fToler2 = BRep_Tool::Tolerance( face );
3719       fToler2 *= fToler2 * 10.;
3720       isUPeriodic = surface->IsUPeriodic();
3721       // if ( isUPeriodic )
3722       //   surface->UPeriod();
3723       isVPeriodic = surface->IsVPeriodic();
3724       // if ( isVPeriodic )
3725       //   surface->VPeriod();
3726       surface->Bounds( u1, u2, v1, v2 );
3727       helper.SetSubShape( face );
3728     }
3729     // ---------------------------------------------------------
3730     // for elements on a face, find movable and fixed nodes and
3731     // compute UV for them
3732     // ---------------------------------------------------------
3733     bool checkBoundaryNodes = false;
3734     bool isQuadratic = false;
3735     set<const SMDS_MeshNode*> setMovableNodes;
3736     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3737     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3738     list< const SMDS_MeshElement* > elemsOnFace;
3739
3740     Extrema_GenExtPS projector;
3741     GeomAdaptor_Surface surfAdaptor;
3742     if ( !surface.IsNull() ) {
3743       surfAdaptor.Load( surface );
3744       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3745     }
3746     int nbElemOnFace = 0;
3747     itElem = theElems.begin();
3748     // loop on not yet smoothed elements: look for elems on a face
3749     while ( itElem != theElems.end() )
3750     {
3751       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3752         break; // all elements found
3753
3754       const SMDS_MeshElement* elem = *itElem;
3755       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3756            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3757         ++itElem;
3758         continue;
3759       }
3760       elemsOnFace.push_back( elem );
3761       theElems.erase( itElem++ );
3762       nbElemOnFace++;
3763
3764       if ( !isQuadratic )
3765         isQuadratic = elem->IsQuadratic();
3766
3767       // get movable nodes of elem
3768       const SMDS_MeshNode* node;
3769       SMDS_TypeOfPosition posType;
3770       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3771       int nn = 0, nbn =  elem->NbNodes();
3772       if(elem->IsQuadratic())
3773         nbn = nbn/2;
3774       while ( nn++ < nbn ) {
3775         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3776         const SMDS_PositionPtr& pos = node->GetPosition();
3777         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3778         if (posType != SMDS_TOP_EDGE &&
3779             posType != SMDS_TOP_VERTEX &&
3780             theFixedNodes.find( node ) == theFixedNodes.end())
3781         {
3782           // check if all faces around the node are on faceSubMesh
3783           // because a node on edge may be bound to face
3784           bool all = true;
3785           if ( faceSubMesh ) {
3786             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3787             while ( eIt->more() && all ) {
3788               const SMDS_MeshElement* e = eIt->next();
3789               all = faceSubMesh->Contains( e );
3790             }
3791           }
3792           if ( all )
3793             setMovableNodes.insert( node );
3794           else
3795             checkBoundaryNodes = true;
3796         }
3797         if ( posType == SMDS_TOP_3DSPACE )
3798           checkBoundaryNodes = true;
3799       }
3800
3801       if ( surface.IsNull() )
3802         continue;
3803
3804       // get nodes to check UV
3805       list< const SMDS_MeshNode* > uvCheckNodes;
3806       const SMDS_MeshNode* nodeInFace = 0;
3807       itN = elem->nodesIterator();
3808       nn = 0; nbn =  elem->NbNodes();
3809       if(elem->IsQuadratic())
3810         nbn = nbn/2;
3811       while ( nn++ < nbn ) {
3812         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3813         if ( node->GetPosition()->GetDim() == 2 )
3814           nodeInFace = node;
3815         if ( uvMap.find( node ) == uvMap.end() )
3816           uvCheckNodes.push_back( node );
3817         // add nodes of elems sharing node
3818         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3819         //         while ( eIt->more() ) {
3820         //           const SMDS_MeshElement* e = eIt->next();
3821         //           if ( e != elem ) {
3822         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3823         //             while ( nIt->more() ) {
3824         //               const SMDS_MeshNode* n =
3825         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3826         //               if ( uvMap.find( n ) == uvMap.end() )
3827         //                 uvCheckNodes.push_back( n );
3828         //             }
3829         //           }
3830         //         }
3831       }
3832       // check UV on face
3833       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3834       for ( ; n != uvCheckNodes.end(); ++n ) {
3835         node = *n;
3836         gp_XY uv( 0, 0 );
3837         const SMDS_PositionPtr& pos = node->GetPosition();
3838         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3839         // get existing UV
3840         if ( pos )
3841         {
3842           bool toCheck = true;
3843           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3844         }
3845         // compute not existing UV
3846         bool project = ( posType == SMDS_TOP_3DSPACE );
3847         // double dist1 = DBL_MAX, dist2 = 0;
3848         // if ( posType != SMDS_TOP_3DSPACE ) {
3849         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3850         //   project = dist1 > fToler2;
3851         // }
3852         if ( project ) { // compute new UV
3853           gp_XY newUV;
3854           gp_Pnt pNode = SMESH_NodeXYZ( node );
3855           if ( !getClosestUV( projector, pNode, newUV )) {
3856             MESSAGE("Node Projection Failed " << node);
3857           }
3858           else {
3859             if ( isUPeriodic )
3860               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3861             if ( isVPeriodic )
3862               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3863             // check new UV
3864             // if ( posType != SMDS_TOP_3DSPACE )
3865             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3866             // if ( dist2 < dist1 )
3867             uv = newUV;
3868           }
3869         }
3870         // store UV in the map
3871         listUV.push_back( uv );
3872         uvMap.insert( make_pair( node, &listUV.back() ));
3873       }
3874     } // loop on not yet smoothed elements
3875
3876     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3877       checkBoundaryNodes = true;
3878
3879     // fix nodes on mesh boundary
3880
3881     if ( checkBoundaryNodes ) {
3882       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3883       map< SMESH_TLink, int >::iterator link_nb;
3884       // put all elements links to linkNbMap
3885       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3886       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3887         const SMDS_MeshElement* elem = (*elemIt);
3888         int nbn =  elem->NbCornerNodes();
3889         // loop on elem links: insert them in linkNbMap
3890         for ( int iN = 0; iN < nbn; ++iN ) {
3891           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3892           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3893           SMESH_TLink link( n1, n2 );
3894           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3895           link_nb->second++;
3896         }
3897       }
3898       // remove nodes that are in links encountered only once from setMovableNodes
3899       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3900         if ( link_nb->second == 1 ) {
3901           setMovableNodes.erase( link_nb->first.node1() );
3902           setMovableNodes.erase( link_nb->first.node2() );
3903         }
3904       }
3905     }
3906
3907     // -----------------------------------------------------
3908     // for nodes on seam edge, compute one more UV ( uvMap2 );
3909     // find movable nodes linked to nodes on seam and which
3910     // are to be smoothed using the second UV ( uvMap2 )
3911     // -----------------------------------------------------
3912
3913     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3914     if ( !surface.IsNull() ) {
3915       TopExp_Explorer eExp( face, TopAbs_EDGE );
3916       for ( ; eExp.More(); eExp.Next() ) {
3917         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3918         if ( !BRep_Tool::IsClosed( edge, face ))
3919           continue;
3920         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3921         if ( !sm ) continue;
3922         // find out which parameter varies for a node on seam
3923         double f,l;
3924         gp_Pnt2d uv1, uv2;
3925         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3926         if ( pcurve.IsNull() ) continue;
3927         uv1 = pcurve->Value( f );
3928         edge.Reverse();
3929         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3930         if ( pcurve.IsNull() ) continue;
3931         uv2 = pcurve->Value( f );
3932         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3933         // assure uv1 < uv2
3934         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3935           std::swap( uv1, uv2 );
3936         // get nodes on seam and its vertices
3937         list< const SMDS_MeshNode* > seamNodes;
3938         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3939         while ( nSeamIt->more() ) {
3940           const SMDS_MeshNode* node = nSeamIt->next();
3941           if ( !isQuadratic || !IsMedium( node ))
3942             seamNodes.push_back( node );
3943         }
3944         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3945         for ( ; vExp.More(); vExp.Next() ) {
3946           sm = aMesh->MeshElements( vExp.Current() );
3947           if ( sm ) {
3948             nSeamIt = sm->GetNodes();
3949             while ( nSeamIt->more() )
3950               seamNodes.push_back( nSeamIt->next() );
3951           }
3952         }
3953         // loop on nodes on seam
3954         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3955         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3956           const SMDS_MeshNode* nSeam = *noSeIt;
3957           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3958           if ( n_uv == uvMap.end() )
3959             continue;
3960           // set the first UV
3961           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3962           // set the second UV
3963           listUV.push_back( *n_uv->second );
3964           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3965           if ( uvMap2.empty() )
3966             uvMap2 = uvMap; // copy the uvMap contents
3967           uvMap2[ nSeam ] = &listUV.back();
3968
3969           // collect movable nodes linked to ones on seam in nodesNearSeam
3970           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3971           while ( eIt->more() ) {
3972             const SMDS_MeshElement* e = eIt->next();
3973             int nbUseMap1 = 0, nbUseMap2 = 0;
3974             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3975             int nn = 0, nbn =  e->NbNodes();
3976             if(e->IsQuadratic()) nbn = nbn/2;
3977             while ( nn++ < nbn )
3978             {
3979               const SMDS_MeshNode* n =
3980                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3981               if (n == nSeam ||
3982                   setMovableNodes.find( n ) == setMovableNodes.end() )
3983                 continue;
3984               // add only nodes being closer to uv2 than to uv1
3985               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3986               //              0.5 * ( n->Y() + nSeam->Y() ),
3987               //              0.5 * ( n->Z() + nSeam->Z() ));
3988               // gp_XY uv;
3989               // getClosestUV( projector, pMid, uv );
3990               double x = uvMap[ n ]->Coord( iPar );
3991               if ( Abs( uv1.Coord( iPar ) - x ) >
3992                    Abs( uv2.Coord( iPar ) - x )) {
3993                 nodesNearSeam.insert( n );
3994                 nbUseMap2++;
3995               }
3996               else
3997                 nbUseMap1++;
3998             }
3999             // for centroidalSmooth all element nodes must
4000             // be on one side of a seam
4001             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4002               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4003               nn = 0;
4004               while ( nn++ < nbn ) {
4005                 const SMDS_MeshNode* n =
4006                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4007                 setMovableNodes.erase( n );
4008               }
4009             }
4010           }
4011         } // loop on nodes on seam
4012       } // loop on edge of a face
4013     } // if ( !face.IsNull() )
4014
4015     if ( setMovableNodes.empty() ) {
4016       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4017       continue; // goto next face
4018     }
4019
4020     // -------------
4021     // SMOOTHING //
4022     // -------------
4023
4024     int it = -1;
4025     double maxRatio = -1., maxDisplacement = -1.;
4026     set<const SMDS_MeshNode*>::iterator nodeToMove;
4027     for ( it = 0; it < theNbIterations; it++ ) {
4028       maxDisplacement = 0.;
4029       nodeToMove = setMovableNodes.begin();
4030       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4031         const SMDS_MeshNode* node = (*nodeToMove);
4032         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4033
4034         // smooth
4035         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4036         if ( theSmoothMethod == LAPLACIAN )
4037           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4038         else
4039           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4040
4041         // node displacement
4042         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4043         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4044         if ( aDispl > maxDisplacement )
4045           maxDisplacement = aDispl;
4046       }
4047       // no node movement => exit
4048       //if ( maxDisplacement < 1.e-16 ) {
4049       if ( maxDisplacement < disttol ) {
4050         MESSAGE("-- no node movement --");
4051         break;
4052       }
4053
4054       // check elements quality
4055       maxRatio  = 0;
4056       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4057       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4058         const SMDS_MeshElement* elem = (*elemIt);
4059         if ( !elem || elem->GetType() != SMDSAbs_Face )
4060           continue;
4061         SMESH::Controls::TSequenceOfXYZ aPoints;
4062         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4063           double aValue = aQualityFunc.GetValue( aPoints );
4064           if ( aValue > maxRatio )
4065             maxRatio = aValue;
4066         }
4067       }
4068       if ( maxRatio <= theTgtAspectRatio ) {
4069         //MESSAGE("-- quality achieved --");
4070         break;
4071       }
4072       if (it+1 == theNbIterations) {
4073         //MESSAGE("-- Iteration limit exceeded --");
4074       }
4075     } // smoothing iterations
4076
4077     // MESSAGE(" Face id: " << *fId <<
4078     //         " Nb iterstions: " << it <<
4079     //         " Displacement: " << maxDisplacement <<
4080     //         " Aspect Ratio " << maxRatio);
4081
4082     // ---------------------------------------
4083     // new nodes positions are computed,
4084     // record movement in DS and set new UV
4085     // ---------------------------------------
4086     nodeToMove = setMovableNodes.begin();
4087     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4088       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4089       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4090       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4091       if ( node_uv != uvMap.end() ) {
4092         gp_XY* uv = node_uv->second;
4093         node->SetPosition
4094           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4095       }
4096     }
4097
4098     // move medium nodes of quadratic elements
4099     if ( isQuadratic )
4100     {
4101       vector<const SMDS_MeshNode*> nodes;
4102       bool checkUV;
4103       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4104       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4105       {
4106         const SMDS_MeshElement* QF = *elemIt;
4107         if ( QF->IsQuadratic() )
4108         {
4109           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4110                         SMDS_MeshElement::iterator() );
4111           nodes.push_back( nodes[0] );
4112           gp_Pnt xyz;
4113           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4114           {
4115             if ( !surface.IsNull() )
4116             {
4117               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4118               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4119               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4120               xyz = surface->Value( uv.X(), uv.Y() );
4121             }
4122             else {
4123               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4124             }
4125             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4126               // we have to move a medium node
4127               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4128           }
4129         }
4130       }
4131     }
4132
4133   } // loop on face ids
4134
4135 }
4136
4137 namespace
4138 {
4139   //=======================================================================
4140   //function : isReverse
4141   //purpose  : Return true if normal of prevNodes is not co-directied with
4142   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4143   //           iNotSame is where prevNodes and nextNodes are different.
4144   //           If result is true then future volume orientation is OK
4145   //=======================================================================
4146
4147   bool isReverse(const SMDS_MeshElement*             face,
4148                  const vector<const SMDS_MeshNode*>& prevNodes,
4149                  const vector<const SMDS_MeshNode*>& nextNodes,
4150                  const int                           iNotSame)
4151   {
4152
4153     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4154     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4155     gp_XYZ extrDir( pN - pP ), faceNorm;
4156     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4157
4158     return faceNorm * extrDir < 0.0;
4159   }
4160
4161   //================================================================================
4162   /*!
4163    * \brief Assure that theElemSets[0] holds elements, not nodes
4164    */
4165   //================================================================================
4166
4167   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4168   {
4169     if ( !theElemSets[0].empty() &&
4170          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4171     {
4172       std::swap( theElemSets[0], theElemSets[1] );
4173     }
4174     else if ( !theElemSets[1].empty() &&
4175               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4176     {
4177       std::swap( theElemSets[0], theElemSets[1] );
4178     }
4179   }
4180 }
4181
4182 //=======================================================================
4183 /*!
4184  * \brief Create elements by sweeping an element
4185  * \param elem - element to sweep
4186  * \param newNodesItVec - nodes generated from each node of the element
4187  * \param newElems - generated elements
4188  * \param nbSteps - number of sweeping steps
4189  * \param srcElements - to append elem for each generated element
4190  */
4191 //=======================================================================
4192
4193 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4194                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4195                                     list<const SMDS_MeshElement*>&        newElems,
4196                                     const size_t                          nbSteps,
4197                                     SMESH_SequenceOfElemPtr&              srcElements)
4198 {
4199   SMESHDS_Mesh* aMesh = GetMeshDS();
4200
4201   const int           nbNodes = elem->NbNodes();
4202   const int         nbCorners = elem->NbCornerNodes();
4203   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4204                                                           polyhedron creation !!! */
4205   // Loop on elem nodes:
4206   // find new nodes and detect same nodes indices
4207   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4208   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4209   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4210   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4211
4212   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4213   vector<int> sames(nbNodes);
4214   vector<bool> isSingleNode(nbNodes);
4215
4216   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4217     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4218     const SMDS_MeshNode*                         node = nnIt->first;
4219     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4220     if ( listNewNodes.empty() )
4221       return;
4222
4223     itNN   [ iNode ] = listNewNodes.begin();
4224     prevNod[ iNode ] = node;
4225     nextNod[ iNode ] = listNewNodes.front();
4226
4227     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4228                                                              corner node of linear */
4229     if ( prevNod[ iNode ] != nextNod [ iNode ])
4230       nbDouble += !isSingleNode[iNode];
4231
4232     if( iNode < nbCorners ) { // check corners only
4233       if ( prevNod[ iNode ] == nextNod [ iNode ])
4234         sames[nbSame++] = iNode;
4235       else
4236         iNotSameNode = iNode;
4237     }
4238   }
4239
4240   if ( nbSame == nbNodes || nbSame > 2) {
4241     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4242     return;
4243   }
4244
4245   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4246   {
4247     // fix nodes order to have bottom normal external
4248     if ( baseType == SMDSEntity_Polygon )
4249     {
4250       std::reverse( itNN.begin(), itNN.end() );
4251       std::reverse( prevNod.begin(), prevNod.end() );
4252       std::reverse( midlNod.begin(), midlNod.end() );
4253       std::reverse( nextNod.begin(), nextNod.end() );
4254       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4255     }
4256     else
4257     {
4258       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4259       SMDS_MeshCell::applyInterlace( ind, itNN );
4260       SMDS_MeshCell::applyInterlace( ind, prevNod );
4261       SMDS_MeshCell::applyInterlace( ind, nextNod );
4262       SMDS_MeshCell::applyInterlace( ind, midlNod );
4263       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4264       if ( nbSame > 0 )
4265       {
4266         sames[nbSame] = iNotSameNode;
4267         for ( int j = 0; j <= nbSame; ++j )
4268           for ( size_t i = 0; i < ind.size(); ++i )
4269             if ( ind[i] == sames[j] )
4270             {
4271               sames[j] = i;
4272               break;
4273             }
4274         iNotSameNode = sames[nbSame];
4275       }
4276     }
4277   }
4278   else if ( elem->GetType() == SMDSAbs_Edge )
4279   {
4280     // orient a new face same as adjacent one
4281     int i1, i2;
4282     const SMDS_MeshElement* e;
4283     TIDSortedElemSet dummy;
4284     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4285         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4286         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4287     {
4288       // there is an adjacent face, check order of nodes in it
4289       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4290       if ( sameOrder )
4291       {
4292         std::swap( itNN[0],    itNN[1] );
4293         std::swap( prevNod[0], prevNod[1] );
4294         std::swap( nextNod[0], nextNod[1] );
4295         std::swap( isSingleNode[0], isSingleNode[1] );
4296         if ( nbSame > 0 )
4297           sames[0] = 1 - sames[0];
4298         iNotSameNode = 1 - iNotSameNode;
4299       }
4300     }
4301   }
4302
4303   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4304   if ( nbSame > 0 ) {
4305     iSameNode    = sames[ nbSame-1 ];
4306     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4307     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4308     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4309   }
4310
4311   if ( baseType == SMDSEntity_Polygon )
4312   {
4313     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4314     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4315   }
4316   else if ( baseType == SMDSEntity_Quad_Polygon )
4317   {
4318     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4319     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4320   }
4321
4322   // make new elements
4323   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4324   {
4325     // get next nodes
4326     for ( iNode = 0; iNode < nbNodes; iNode++ )
4327     {
4328       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4329       nextNod[ iNode ] = *itNN[ iNode ]++;
4330     }
4331
4332     SMDS_MeshElement* aNewElem = 0;
4333     /*if(!elem->IsPoly())*/ {
4334       switch ( baseType ) {
4335       case SMDSEntity_0D:
4336       case SMDSEntity_Node: { // sweep NODE
4337         if ( nbSame == 0 ) {
4338           if ( isSingleNode[0] )
4339             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4340           else
4341             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4342         }
4343         else
4344           return;
4345         break;
4346       }
4347       case SMDSEntity_Edge: { // sweep EDGE
4348         if ( nbDouble == 0 )
4349         {
4350           if ( nbSame == 0 ) // ---> quadrangle
4351             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4352                                       nextNod[ 1 ], nextNod[ 0 ] );
4353           else               // ---> triangle
4354             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4355                                       nextNod[ iNotSameNode ] );
4356         }
4357         else                 // ---> polygon
4358         {
4359           vector<const SMDS_MeshNode*> poly_nodes;
4360           poly_nodes.push_back( prevNod[0] );
4361           poly_nodes.push_back( prevNod[1] );
4362           if ( prevNod[1] != nextNod[1] )
4363           {
4364             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4365             poly_nodes.push_back( nextNod[1] );
4366           }
4367           if ( prevNod[0] != nextNod[0] )
4368           {
4369             poly_nodes.push_back( nextNod[0] );
4370             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4371           }
4372           switch ( poly_nodes.size() ) {
4373           case 3:
4374             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4375             break;
4376           case 4:
4377             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4378                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4379             break;
4380           default:
4381             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4382           }
4383         }
4384         break;
4385       }
4386       case SMDSEntity_Triangle: // TRIANGLE --->
4387       {
4388         if ( nbDouble > 0 ) break;
4389         if ( nbSame == 0 )       // ---> pentahedron
4390           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4391                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4392
4393         else if ( nbSame == 1 )  // ---> pyramid
4394           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4395                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4396                                        nextNod[ iSameNode ]);
4397
4398         else // 2 same nodes:       ---> tetrahedron
4399           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4400                                        nextNod[ iNotSameNode ]);
4401         break;
4402       }
4403       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4404       {
4405         if ( nbSame == 2 )
4406           return;
4407         if ( nbDouble+nbSame == 2 )
4408         {
4409           if(nbSame==0) {      // ---> quadratic quadrangle
4410             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4411                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4412           }
4413           else { //(nbSame==1) // ---> quadratic triangle
4414             if(sames[0]==2) {
4415               return; // medium node on axis
4416             }
4417             else if(sames[0]==0)
4418               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4419                                         prevNod[2], midlNod[1], nextNod[2] );
4420             else // sames[0]==1
4421               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4422                                         prevNod[2], nextNod[2], midlNod[0]);
4423           }
4424         }
4425         else if ( nbDouble == 3 )
4426         {
4427           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4428             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4429                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4430           }
4431         }
4432         else
4433           return;
4434         break;
4435       }
4436       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4437         if ( nbDouble > 0 ) break;
4438
4439         if ( nbSame == 0 )       // ---> hexahedron
4440           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4441                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4442
4443         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4444           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4445                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4446                                        nextNod[ iSameNode ]);
4447           newElems.push_back( aNewElem );
4448           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4449                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4450                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4451         }
4452         else if ( nbSame == 2 ) { // ---> pentahedron
4453           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4454             // iBeforeSame is same too
4455             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4456                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4457                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4458           else
4459             // iAfterSame is same too
4460             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4461                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4462                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4463         }
4464         break;
4465       }
4466       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4467       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4468         if ( nbDouble+nbSame != 3 ) break;
4469         if(nbSame==0) {
4470           // --->  pentahedron with 15 nodes
4471           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4472                                        nextNod[0], nextNod[1], nextNod[2],
4473                                        prevNod[3], prevNod[4], prevNod[5],
4474                                        nextNod[3], nextNod[4], nextNod[5],
4475                                        midlNod[0], midlNod[1], midlNod[2]);
4476         }
4477         else if(nbSame==1) {
4478           // --->  2d order pyramid of 13 nodes
4479           int apex = iSameNode;
4480           int i0 = ( apex + 1 ) % nbCorners;
4481           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4482           int i0a = apex + 3;
4483           int i1a = i1 + 3;
4484           int i01 = i0 + 3;
4485           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4486                                       nextNod[i0], nextNod[i1], prevNod[apex],
4487                                       prevNod[i01], midlNod[i0],
4488                                       nextNod[i01], midlNod[i1],
4489                                       prevNod[i1a], prevNod[i0a],
4490                                       nextNod[i0a], nextNod[i1a]);
4491         }
4492         else if(nbSame==2) {
4493           // --->  2d order tetrahedron of 10 nodes
4494           int n1 = iNotSameNode;
4495           int n2 = ( n1 + 1             ) % nbCorners;
4496           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4497           int n12 = n1 + 3;
4498           int n23 = n2 + 3;
4499           int n31 = n3 + 3;
4500           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4501                                        prevNod[n12], prevNod[n23], prevNod[n31],
4502                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4503         }
4504         break;
4505       }
4506       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4507         if( nbSame == 0 ) {
4508           if ( nbDouble != 4 ) break;
4509           // --->  hexahedron with 20 nodes
4510           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4511                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4512                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4513                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4514                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4515         }
4516         else if(nbSame==1) {
4517           // ---> pyramid + pentahedron - can not be created since it is needed
4518           // additional middle node at the center of face
4519           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4520           return;
4521         }
4522         else if( nbSame == 2 ) {
4523           if ( nbDouble != 2 ) break;
4524           // --->  2d order Pentahedron with 15 nodes
4525           int n1,n2,n4,n5;
4526           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4527             // iBeforeSame is same too
4528             n1 = iBeforeSame;
4529             n2 = iOpposSame;
4530             n4 = iSameNode;
4531             n5 = iAfterSame;
4532           }
4533           else {
4534             // iAfterSame is same too
4535             n1 = iSameNode;
4536             n2 = iBeforeSame;
4537             n4 = iAfterSame;
4538             n5 = iOpposSame;
4539           }
4540           int n12 = n2 + 4;
4541           int n45 = n4 + 4;
4542           int n14 = n1 + 4;
4543           int n25 = n5 + 4;
4544           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4545                                        prevNod[n4], prevNod[n5], nextNod[n5],
4546                                        prevNod[n12], midlNod[n2], nextNod[n12],
4547                                        prevNod[n45], midlNod[n5], nextNod[n45],
4548                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4549         }
4550         break;
4551       }
4552       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4553
4554         if( nbSame == 0 && nbDouble == 9 ) {
4555           // --->  tri-quadratic hexahedron with 27 nodes
4556           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4557                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4558                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4559                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4560                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4561                                        prevNod[8], // bottom center
4562                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4563                                        nextNod[8], // top center
4564                                        midlNod[8]);// elem center
4565         }
4566         else
4567         {
4568           return;
4569         }
4570         break;
4571       }
4572       case SMDSEntity_Polygon: { // sweep POLYGON
4573
4574         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4575           // --->  hexagonal prism
4576           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4577                                        prevNod[3], prevNod[4], prevNod[5],
4578                                        nextNod[0], nextNod[1], nextNod[2],
4579                                        nextNod[3], nextNod[4], nextNod[5]);
4580         }
4581         break;
4582       }
4583       case SMDSEntity_Ball:
4584         return;
4585
4586       default:
4587         break;
4588       } // switch ( baseType )
4589     } // scope
4590
4591     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4592     {
4593       if ( baseType != SMDSEntity_Polygon )
4594       {
4595         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4596         SMDS_MeshCell::applyInterlace( ind, prevNod );
4597         SMDS_MeshCell::applyInterlace( ind, nextNod );
4598         SMDS_MeshCell::applyInterlace( ind, midlNod );
4599         SMDS_MeshCell::applyInterlace( ind, itNN );
4600         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4601         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4602       }
4603       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4604       vector<int> quantities (nbNodes + 2);
4605       polyedre_nodes.clear();
4606       quantities.clear();
4607
4608       // bottom of prism
4609       for (int inode = 0; inode < nbNodes; inode++)
4610         polyedre_nodes.push_back( prevNod[inode] );
4611       quantities.push_back( nbNodes );
4612
4613       // top of prism
4614       polyedre_nodes.push_back( nextNod[0] );
4615       for (int inode = nbNodes; inode-1; --inode )
4616         polyedre_nodes.push_back( nextNod[inode-1] );
4617       quantities.push_back( nbNodes );
4618
4619       // side faces
4620       // 3--6--2
4621       // |     |
4622       // 7     5
4623       // |     |
4624       // 0--4--1
4625       const int iQuad = elem->IsQuadratic();
4626       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4627       {
4628         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4629         int inextface = (iface+1+iQuad) % nbNodes;
4630         int imid      = (iface+1) % nbNodes;
4631         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4632         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4633         polyedre_nodes.push_back( prevNod[iface] );             // 1
4634         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4635         {
4636           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4637           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4638         }
4639         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4640         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4641         {
4642           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4643           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4644         }
4645         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4646         if ( nbFaceNodes > 2 )
4647           quantities.push_back( nbFaceNodes );
4648         else // degenerated face
4649           polyedre_nodes.resize( prevNbNodes );
4650       }
4651       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4652
4653     } // try to create a polyherdal prism
4654
4655     if ( aNewElem ) {
4656       newElems.push_back( aNewElem );
4657       myLastCreatedElems.push_back(aNewElem);
4658       srcElements.push_back( elem );
4659     }
4660
4661     // set new prev nodes
4662     for ( iNode = 0; iNode < nbNodes; iNode++ )
4663       prevNod[ iNode ] = nextNod[ iNode ];
4664
4665   } // loop on steps
4666 }
4667
4668 //=======================================================================
4669 /*!
4670  * \brief Create 1D and 2D elements around swept elements
4671  * \param mapNewNodes - source nodes and ones generated from them
4672  * \param newElemsMap - source elements and ones generated from them
4673  * \param elemNewNodesMap - nodes generated from each node of each element
4674  * \param elemSet - all swept elements
4675  * \param nbSteps - number of sweeping steps
4676  * \param srcElements - to append elem for each generated element
4677  */
4678 //=======================================================================
4679
4680 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4681                                   TTElemOfElemListMap &    newElemsMap,
4682                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4683                                   TIDSortedElemSet&        elemSet,
4684                                   const int                nbSteps,
4685                                   SMESH_SequenceOfElemPtr& srcElements)
4686 {
4687   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4688   SMESHDS_Mesh* aMesh = GetMeshDS();
4689
4690   // Find nodes belonging to only one initial element - sweep them into edges.
4691
4692   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4693   for ( ; nList != mapNewNodes.end(); nList++ )
4694   {
4695     const SMDS_MeshNode* node =
4696       static_cast<const SMDS_MeshNode*>( nList->first );
4697     if ( newElemsMap.count( node ))
4698       continue; // node was extruded into edge
4699     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4700     int nbInitElems = 0;
4701     const SMDS_MeshElement* el = 0;
4702     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4703     while ( eIt->more() && nbInitElems < 2 ) {
4704       const SMDS_MeshElement* e = eIt->next();
4705       SMDSAbs_ElementType  type = e->GetType();
4706       if ( type == SMDSAbs_Volume ||
4707            type < highType ||
4708            !elemSet.count(e))
4709         continue;
4710       if ( type > highType ) {
4711         nbInitElems = 0;
4712         highType    = type;
4713       }
4714       el = e;
4715       ++nbInitElems;
4716     }
4717     if ( nbInitElems == 1 ) {
4718       bool NotCreateEdge = el && el->IsMediumNode(node);
4719       if(!NotCreateEdge) {
4720         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4721         list<const SMDS_MeshElement*> newEdges;
4722         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4723       }
4724     }
4725   }
4726
4727   // Make a ceiling for each element ie an equal element of last new nodes.
4728   // Find free links of faces - make edges and sweep them into faces.
4729
4730   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4731
4732   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4733   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4734   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4735   {
4736     const SMDS_MeshElement* elem = itElem->first;
4737     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4738
4739     if(itElem->second.size()==0) continue;
4740
4741     const bool isQuadratic = elem->IsQuadratic();
4742
4743     if ( elem->GetType() == SMDSAbs_Edge ) {
4744       // create a ceiling edge
4745       if ( !isQuadratic ) {
4746         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4747                                vecNewNodes[ 1 ]->second.back())) {
4748           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4749                                                       vecNewNodes[ 1 ]->second.back()));
4750           srcElements.push_back( elem );
4751         }
4752       }
4753       else {
4754         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4755                                vecNewNodes[ 1 ]->second.back(),
4756                                vecNewNodes[ 2 ]->second.back())) {
4757           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4758                                                       vecNewNodes[ 1 ]->second.back(),
4759                                                       vecNewNodes[ 2 ]->second.back()));
4760           srcElements.push_back( elem );
4761         }
4762       }
4763     }
4764     if ( elem->GetType() != SMDSAbs_Face )
4765       continue;
4766
4767     bool hasFreeLinks = false;
4768
4769     TIDSortedElemSet avoidSet;
4770     avoidSet.insert( elem );
4771
4772     set<const SMDS_MeshNode*> aFaceLastNodes;
4773     int iNode, nbNodes = vecNewNodes.size();
4774     if ( !isQuadratic ) {
4775       // loop on the face nodes
4776       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4777         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778         // look for free links of the face
4779         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4780         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782         // check if a link n1-n2 is free
4783         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4784           hasFreeLinks = true;
4785           // make a new edge and a ceiling for a new edge
4786           const SMDS_MeshElement* edge;
4787           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4788             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4789             srcElements.push_back( myLastCreatedElems.back() );
4790           }
4791           n1 = vecNewNodes[ iNode ]->second.back();
4792           n2 = vecNewNodes[ iNext ]->second.back();
4793           if ( !aMesh->FindEdge( n1, n2 )) {
4794             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4795             srcElements.push_back( edge );
4796           }
4797         }
4798       }
4799     }
4800     else { // elem is quadratic face
4801       int nbn = nbNodes/2;
4802       for ( iNode = 0; iNode < nbn; iNode++ ) {
4803         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4804         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4805         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4806         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4807         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4808         // check if a link is free
4809         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4810              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4811              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4812           hasFreeLinks = true;
4813           // make an edge and a ceiling for a new edge
4814           // find medium node
4815           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4816             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4817             srcElements.push_back( elem );
4818           }
4819           n1 = vecNewNodes[ iNode ]->second.back();
4820           n2 = vecNewNodes[ iNext ]->second.back();
4821           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4822           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4823             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4824             srcElements.push_back( elem );
4825           }
4826         }
4827       }
4828       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4829         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4830       }
4831     }
4832
4833     // sweep free links into faces
4834
4835     if ( hasFreeLinks ) {
4836       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4837       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4838
4839       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4840       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4841       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4842         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4843         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4844       }
4845       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4846         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4847         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4848       }
4849       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4850         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4851         std::advance( v, volNb );
4852         // find indices of free faces of a volume and their source edges
4853         list< int > freeInd;
4854         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4855         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4856         int iF, nbF = vTool.NbFaces();
4857         for ( iF = 0; iF < nbF; iF ++ ) {
4858           if ( vTool.IsFreeFace( iF ) &&
4859                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4860                initNodeSet != faceNodeSet) // except an initial face
4861           {
4862             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4863               continue;
4864             if ( faceNodeSet == initNodeSetNoCenter )
4865               continue;
4866             freeInd.push_back( iF );
4867             // find source edge of a free face iF
4868             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4869             vector<const SMDS_MeshNode*>::iterator lastCommom;
4870             commonNodes.resize( nbNodes, 0 );
4871             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4872                                                 initNodeSet.begin(), initNodeSet.end(),
4873                                                 commonNodes.begin());
4874             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4875               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4876             else
4877               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4878 #ifdef _DEBUG_
4879             if ( !srcEdges.back() )
4880             {
4881               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4882                    << iF << " of volume #" << vTool.ID() << endl;
4883             }
4884 #endif
4885           }
4886         }
4887         if ( freeInd.empty() )
4888           continue;
4889
4890         // create wall faces for all steps;
4891         // if such a face has been already created by sweep of edge,
4892         // assure that its orientation is OK
4893         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4894         {
4895           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4896           vTool.SetExternalNormal();
4897           const int nextShift = vTool.IsForward() ? +1 : -1;
4898           list< int >::iterator ind = freeInd.begin();
4899           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4900           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4901           {
4902             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4903             int nbn = vTool.NbFaceNodes( *ind );
4904             const SMDS_MeshElement * f = 0;
4905             if ( nbn == 3 )              ///// triangle
4906             {
4907               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4908               if ( !f ||
4909                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4910               {
4911                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4912                                                      nodes[ 1 ],
4913                                                      nodes[ 1 + nextShift ] };
4914                 if ( f )
4915                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4916                 else
4917                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4918                                                                newOrder[ 2 ] ));
4919               }
4920             }
4921             else if ( nbn == 4 )       ///// quadrangle
4922             {
4923               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4924               if ( !f ||
4925                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4926               {
4927                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4928                                                      nodes[ 2 ], nodes[ 2+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 ], newOrder[ 3 ]));
4934               }
4935             }
4936             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4937             {
4938               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4939               if ( !f ||
4940                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4943                                                      nodes[2],
4944                                                      nodes[2 + 2*nextShift],
4945                                                      nodes[3 - 2*nextShift],
4946                                                      nodes[3],
4947                                                      nodes[3 + 2*nextShift]};
4948                 if ( f )
4949                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4950                 else
4951                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4952                                                                newOrder[ 1 ],
4953                                                                newOrder[ 2 ],
4954                                                                newOrder[ 3 ],
4955                                                                newOrder[ 4 ],
4956                                                                newOrder[ 5 ] ));
4957               }
4958             }
4959             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4960             {
4961               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4962                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4963               if ( !f ||
4964                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4965               {
4966                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4967                                                      nodes[4 - 2*nextShift],
4968                                                      nodes[4],
4969                                                      nodes[4 + 2*nextShift],
4970                                                      nodes[1],
4971                                                      nodes[5 - 2*nextShift],
4972                                                      nodes[5],
4973                                                      nodes[5 + 2*nextShift] };
4974                 if ( f )
4975                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4976                 else
4977                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978                                                               newOrder[ 2 ], newOrder[ 3 ],
4979                                                               newOrder[ 4 ], newOrder[ 5 ],
4980                                                               newOrder[ 6 ], newOrder[ 7 ]));
4981               }
4982             }
4983             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4984             {
4985               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4986                                       SMDSAbs_Face, /*noMedium=*/false);
4987               if ( !f ||
4988                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4989               {
4990                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4991                                                      nodes[4 - 2*nextShift],
4992                                                      nodes[4],
4993                                                      nodes[4 + 2*nextShift],
4994                                                      nodes[1],
4995                                                      nodes[5 - 2*nextShift],
4996                                                      nodes[5],
4997                                                      nodes[5 + 2*nextShift],
4998                                                      nodes[8] };
4999                 if ( f )
5000                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5001                 else
5002                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5003                                                               newOrder[ 2 ], newOrder[ 3 ],
5004                                                               newOrder[ 4 ], newOrder[ 5 ],
5005                                                               newOrder[ 6 ], newOrder[ 7 ],
5006                                                               newOrder[ 8 ]));
5007               }
5008             }
5009             else  //////// polygon
5010             {
5011               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5012               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5013               if ( !f ||
5014                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5015               {
5016                 if ( !vTool.IsForward() )
5017                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5018                 if ( f )
5019                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5020                 else
5021                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5022               }
5023             }
5024
5025             while ( srcElements.size() < myLastCreatedElems.size() )
5026               srcElements.push_back( *srcEdge );
5027
5028           }  // loop on free faces
5029
5030           // go to the next volume
5031           iVol = 0;
5032           while ( iVol++ < nbVolumesByStep ) v++;
5033
5034         } // loop on steps
5035       } // loop on volumes of one step
5036     } // sweep free links into faces
5037
5038     // Make a ceiling face with a normal external to a volume
5039
5040     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5041     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5042     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5043
5044     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5045       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5046       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5047     }
5048     if ( iF >= 0 )
5049     {
5050       lastVol.SetExternalNormal();
5051       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5052       const               int nbn = lastVol.NbFaceNodes( iF );
5053       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5054       if ( !hasFreeLinks ||
5055            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5056       {
5057         const vector<int>& interlace =
5058           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5059         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5060
5061         AddElement( nodeVec, anyFace.Init( elem ));
5062
5063         while ( srcElements.size() < myLastCreatedElems.size() )
5064           srcElements.push_back( elem );
5065       }
5066     }
5067   } // loop on swept elements
5068 }
5069
5070 //=======================================================================
5071 //function : RotationSweep
5072 //purpose  :
5073 //=======================================================================
5074
5075 SMESH_MeshEditor::PGroupIDs
5076 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5077                                 const gp_Ax1&      theAxis,
5078                                 const double       theAngle,
5079                                 const int          theNbSteps,
5080                                 const double       theTol,
5081                                 const bool         theMakeGroups,
5082                                 const bool         theMakeWalls)
5083 {
5084   ClearLastCreated();
5085
5086   setElemsFirst( theElemSets );
5087   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5088   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5089
5090   // source elements for each generated one
5091   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5092   srcElems.reserve( theElemSets[0].size() );
5093   srcNodes.reserve( theElemSets[1].size() );
5094
5095   gp_Trsf aTrsf;
5096   aTrsf.SetRotation( theAxis, theAngle );
5097   gp_Trsf aTrsf2;
5098   aTrsf2.SetRotation( theAxis, theAngle/2. );
5099
5100   gp_Lin aLine( theAxis );
5101   double aSqTol = theTol * theTol;
5102
5103   SMESHDS_Mesh* aMesh = GetMeshDS();
5104
5105   TNodeOfNodeListMap mapNewNodes;
5106   TElemOfVecOfNnlmiMap mapElemNewNodes;
5107   TTElemOfElemListMap newElemsMap;
5108
5109   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5110                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5111                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5112   // loop on theElemSets
5113   TIDSortedElemSet::iterator itElem;
5114   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5115   {
5116     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5117     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5118       const SMDS_MeshElement* elem = *itElem;
5119       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5120         continue;
5121       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5122       newNodesItVec.reserve( elem->NbNodes() );
5123
5124       // loop on elem nodes
5125       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5126       while ( itN->more() )
5127       {
5128         const SMDS_MeshNode* node = cast2Node( itN->next() );
5129
5130         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5131         double coord[3];
5132         aXYZ.Coord( coord[0], coord[1], coord[2] );
5133         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5134
5135         // check if a node has been already sweeped
5136         TNodeOfNodeListMapItr nIt =
5137           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5138         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5139         if ( listNewNodes.empty() )
5140         {
5141           // check if we are to create medium nodes between corner ones
5142           bool needMediumNodes = false;
5143           if ( isQuadraticMesh )
5144           {
5145             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5146             while (it->more() && !needMediumNodes )
5147             {
5148               const SMDS_MeshElement* invElem = it->next();
5149               if ( invElem != elem && !theElems.count( invElem )) continue;
5150               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5151               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5152                 needMediumNodes = true;
5153             }
5154           }
5155
5156           // make new nodes
5157           const SMDS_MeshNode * newNode = node;
5158           for ( int i = 0; i < theNbSteps; i++ ) {
5159             if ( !isOnAxis ) {
5160               if ( needMediumNodes )  // create a medium node
5161               {
5162                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5163                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5164                 myLastCreatedNodes.push_back(newNode);
5165                 srcNodes.push_back( node );
5166                 listNewNodes.push_back( newNode );
5167                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5168               }
5169               else {
5170                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5171               }
5172               // create a corner node
5173               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5174               myLastCreatedNodes.push_back(newNode);
5175               srcNodes.push_back( node );
5176               listNewNodes.push_back( newNode );
5177             }
5178             else {
5179               listNewNodes.push_back( newNode );
5180               // if ( needMediumNodes )
5181               //   listNewNodes.push_back( newNode );
5182             }
5183           }
5184         }
5185         newNodesItVec.push_back( nIt );
5186       }
5187       // make new elements
5188       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5189     }
5190   }
5191
5192   if ( theMakeWalls )
5193     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5194
5195   PGroupIDs newGroupIDs;
5196   if ( theMakeGroups )
5197     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5198
5199   return newGroupIDs;
5200 }
5201
5202 //=======================================================================
5203 //function : ExtrusParam
5204 //purpose  : standard construction
5205 //=======================================================================
5206
5207 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5208                                             const int                theNbSteps,
5209                                             const std::list<double>& theScales,
5210                                             const std::list<double>& theAngles,
5211                                             const gp_XYZ*            theBasePoint,
5212                                             const int                theFlags,
5213                                             const double             theTolerance):
5214   myDir( theStep ),
5215   myBaseP( Precision::Infinite(), 0, 0 ),
5216   myFlags( theFlags ),
5217   myTolerance( theTolerance ),
5218   myElemsToUse( NULL )
5219 {
5220   mySteps = new TColStd_HSequenceOfReal;
5221   const double stepSize = theStep.Magnitude();
5222   for (int i=1; i<=theNbSteps; i++ )
5223     mySteps->Append( stepSize );
5224
5225   if ( !theScales.empty() )
5226   {
5227     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5228       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5229
5230     // add medium scales
5231     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5232     myScales.reserve( theNbSteps * 2 );
5233     myScales.push_back( 0.5 * ( *s1 + 1. ));
5234     myScales.push_back( *s1 );
5235     for ( ; s2 != theScales.end(); s1 = s2++ )
5236     {
5237       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5238       myScales.push_back( *s2 );
5239     }
5240   }
5241
5242   if ( !theAngles.empty() )
5243   {
5244     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5245     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5246       linearAngleVariation( theNbSteps, angles );
5247
5248     // accumulate angles
5249     double angle = 0;
5250     int nbAngles = 0;
5251     std::list<double>::iterator a1 = angles.begin(), a2;
5252     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5253     {
5254       angle += *a1;
5255       *a1 = angle;
5256     }
5257     while ( nbAngles++ < theNbSteps )
5258       angles.push_back( angles.back() );
5259
5260     // add medium angles
5261     a2 = angles.begin(), a1 = a2++;
5262     myAngles.push_back( 0.5 * *a1 );
5263     myAngles.push_back( *a1 );
5264     for ( ; a2 != angles.end(); a1 = a2++ )
5265     {
5266       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5267       myAngles.push_back( *a2 );
5268     }
5269   }
5270
5271   if ( theBasePoint )
5272   {
5273     myBaseP = *theBasePoint;
5274   }
5275
5276   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5277       ( theTolerance > 0 ))
5278   {
5279     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5280   }
5281   else
5282   {
5283     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5284   }
5285 }
5286
5287 //=======================================================================
5288 //function : ExtrusParam
5289 //purpose  : steps are given explicitly
5290 //=======================================================================
5291
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5293                                             Handle(TColStd_HSequenceOfReal) theSteps,
5294                                             const int                       theFlags,
5295                                             const double                    theTolerance):
5296   myDir( theDir ),
5297   mySteps( theSteps ),
5298   myFlags( theFlags ),
5299   myTolerance( theTolerance ),
5300   myElemsToUse( NULL )
5301 {
5302   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5303       ( theTolerance > 0 ))
5304   {
5305     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5306   }
5307   else
5308   {
5309     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5310   }
5311 }
5312
5313 //=======================================================================
5314 //function : ExtrusParam
5315 //purpose  : for extrusion by normal
5316 //=======================================================================
5317
5318 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5319                                             const int    theNbSteps,
5320                                             const int    theFlags,
5321                                             const int    theDim ):
5322   myDir( 1,0,0 ),
5323   mySteps( new TColStd_HSequenceOfReal ),
5324   myFlags( theFlags ),
5325   myTolerance( 0 ),
5326   myElemsToUse( NULL )
5327 {
5328   for (int i = 0; i < theNbSteps; i++ )
5329     mySteps->Append( theStepSize );
5330
5331   if ( theDim == 1 )
5332   {
5333     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5334   }
5335   else
5336   {
5337     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5338   }
5339 }
5340
5341 //=======================================================================
5342 //function : ExtrusParam
5343 //purpose  : for extrusion along path
5344 //=======================================================================
5345
5346 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5347                                             const gp_Pnt*                   theBasePoint,
5348                                             const std::list<double>&        theScales,
5349                                             const bool                      theMakeGroups )
5350   : myBaseP( Precision::Infinite(), 0, 0 ),
5351     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5352     myPathPoints( thePoints )
5353 {
5354   if ( theBasePoint )
5355   {
5356     myBaseP = theBasePoint->XYZ();
5357   }
5358
5359   if ( !theScales.empty() )
5360   {
5361     // add medium scales
5362     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5363     myScales.reserve( thePoints.size() * 2 );
5364     myScales.push_back( 0.5 * ( 1. + *s1 ));
5365     myScales.push_back( *s1 );
5366     for ( ; s2 != theScales.end(); s1 = s2++ )
5367     {
5368       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5369       myScales.push_back( *s2 );
5370     }
5371   }
5372
5373   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5374 }
5375
5376 //=======================================================================
5377 //function : ExtrusParam::SetElementsToUse
5378 //purpose  : stores elements to use for extrusion by normal, depending on
5379 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5380 //           define myBaseP for scaling
5381 //=======================================================================
5382
5383 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5384                                                       const TIDSortedElemSet& nodes )
5385 {
5386   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5387
5388   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5389   {
5390     myBaseP.SetCoord( 0.,0.,0. );
5391     TIDSortedElemSet newNodes;
5392
5393     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5394     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5395     {
5396       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5397       TIDSortedElemSet::const_iterator itElem = elements.begin();
5398       for ( ; itElem != elements.end(); itElem++ )
5399       {
5400         const SMDS_MeshElement* elem = *itElem;
5401         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5402         while ( itN->more() ) {
5403           const SMDS_MeshElement* node = itN->next();
5404           if ( newNodes.insert( node ).second )
5405             myBaseP += SMESH_NodeXYZ( node );
5406         }
5407       }
5408     }
5409     myBaseP /= newNodes.size();
5410   }
5411 }
5412
5413 //=======================================================================
5414 //function : ExtrusParam::beginStepIter
5415 //purpose  : prepare iteration on steps
5416 //=======================================================================
5417
5418 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5419 {
5420   myWithMediumNodes = withMediumNodes;
5421   myNextStep = 1;
5422   myCurSteps.clear();
5423 }
5424 //=======================================================================
5425 //function : ExtrusParam::moreSteps
5426 //purpose  : are there more steps?
5427 //=======================================================================
5428
5429 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5430 {
5431   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5432 }
5433 //=======================================================================
5434 //function : ExtrusParam::nextStep
5435 //purpose  : returns the next step
5436 //=======================================================================
5437
5438 double SMESH_MeshEditor::ExtrusParam::nextStep()
5439 {
5440   double res = 0;
5441   if ( !myCurSteps.empty() )
5442   {
5443     res = myCurSteps.back();
5444     myCurSteps.pop_back();
5445   }
5446   else if ( myNextStep <= mySteps->Length() )
5447   {
5448     myCurSteps.push_back( mySteps->Value( myNextStep ));
5449     ++myNextStep;
5450     if ( myWithMediumNodes )
5451     {
5452       myCurSteps.back() /= 2.;
5453       myCurSteps.push_back( myCurSteps.back() );
5454     }
5455     res = nextStep();
5456   }
5457   return res;
5458 }
5459
5460 //=======================================================================
5461 //function : ExtrusParam::makeNodesByDir
5462 //purpose  : create nodes for standard extrusion
5463 //=======================================================================
5464
5465 int SMESH_MeshEditor::ExtrusParam::
5466 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5467                 const SMDS_MeshNode*              srcNode,
5468                 std::list<const SMDS_MeshNode*> & newNodes,
5469                 const bool                        makeMediumNodes)
5470 {
5471   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5472
5473   int nbNodes = 0;
5474   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5475   {
5476     p += myDir.XYZ() * nextStep();
5477     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5478     newNodes.push_back( newNode );
5479   }
5480
5481   if ( !myScales.empty() || !myAngles.empty() )
5482   {
5483     gp_XYZ  center = myBaseP;
5484     gp_Ax1  ratationAxis( center, myDir );
5485     gp_Trsf rotation;
5486
5487     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5488     size_t i = !makeMediumNodes;
5489     for ( beginStepIter( makeMediumNodes );
5490           moreSteps();
5491           ++nIt, i += 1 + !makeMediumNodes )
5492     {
5493       center += myDir.XYZ() * nextStep();
5494
5495       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5496       bool moved = false;
5497       if ( i < myScales.size() )
5498       {
5499         xyz = ( myScales[i] * ( xyz - center )) + center;
5500         moved = true;
5501       }
5502       if ( !myAngles.empty() )
5503       {
5504         rotation.SetRotation( ratationAxis, myAngles[i] );
5505         rotation.Transforms( xyz );
5506         moved = true;
5507       }
5508       if ( moved )
5509         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5510       else
5511         break;
5512     }
5513   }
5514   return nbNodes;
5515 }
5516
5517 //=======================================================================
5518 //function : ExtrusParam::makeNodesByDirAndSew
5519 //purpose  : create nodes for standard extrusion with sewing
5520 //=======================================================================
5521
5522 int SMESH_MeshEditor::ExtrusParam::
5523 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5524                       const SMDS_MeshNode*              srcNode,
5525                       std::list<const SMDS_MeshNode*> & newNodes,
5526                       const bool                        makeMediumNodes)
5527 {
5528   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5529
5530   int nbNodes = 0;
5531   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5532   {
5533     P1 += myDir.XYZ() * nextStep();
5534
5535     // try to search in sequence of existing nodes
5536     // if myNodes.size()>0 we 'nave to use given sequence
5537     // else - use all nodes of mesh
5538     const SMDS_MeshNode * node = 0;
5539     if ( myNodes.Length() > 0 )
5540     {
5541       for ( int i = 1; i <= myNodes.Length(); i++ )
5542       {
5543         SMESH_NodeXYZ P2 = myNodes.Value(i);
5544         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5545         {
5546           node = myNodes.Value(i);
5547           break;
5548         }
5549       }
5550     }
5551     else
5552     {
5553       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5554       while(itn->more())
5555       {
5556         SMESH_NodeXYZ P2 = itn->next();
5557         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5558         {
5559           node = P2._node;
5560           break;
5561         }
5562       }
5563     }
5564
5565     if ( !node )
5566       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5567
5568     newNodes.push_back( node );
5569
5570   } // loop on steps
5571
5572   return nbNodes;
5573 }
5574
5575 //=======================================================================
5576 //function : ExtrusParam::makeNodesByNormal2D
5577 //purpose  : create nodes for extrusion using normals of faces
5578 //=======================================================================
5579
5580 int SMESH_MeshEditor::ExtrusParam::
5581 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5582                      const SMDS_MeshNode*              srcNode,
5583                      std::list<const SMDS_MeshNode*> & newNodes,
5584                      const bool                        makeMediumNodes)
5585 {
5586   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5587
5588   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5589
5590   // get normals to faces sharing srcNode
5591   vector< gp_XYZ > norms, baryCenters;
5592   gp_XYZ norm, avgNorm( 0,0,0 );
5593   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5594   while ( faceIt->more() )
5595   {
5596     const SMDS_MeshElement* face = faceIt->next();
5597     if ( myElemsToUse && !myElemsToUse->count( face ))
5598       continue;
5599     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5600     {
5601       norms.push_back( norm );
5602       avgNorm += norm;
5603       if ( !alongAvgNorm )
5604       {
5605         gp_XYZ bc(0,0,0);
5606         int nbN = 0;
5607         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5608           bc += SMESH_NodeXYZ( nIt->next() );
5609         baryCenters.push_back( bc / nbN );
5610       }
5611     }
5612   }
5613
5614   if ( norms.empty() ) return 0;
5615
5616   double normSize = avgNorm.Modulus();
5617   if ( normSize < std::numeric_limits<double>::min() )
5618     return 0;
5619
5620   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5621   {
5622     myDir = avgNorm;
5623     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5624   }
5625
5626   avgNorm /= normSize;
5627
5628   int nbNodes = 0;
5629   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5630   {
5631     gp_XYZ pNew = p;
5632     double stepSize = nextStep();
5633
5634     if ( norms.size() > 1 )
5635     {
5636       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5637       {
5638         // translate plane of a face
5639         baryCenters[ iF ] += norms[ iF ] * stepSize;
5640
5641         // find point of intersection of the face plane located at baryCenters[ iF ]
5642         // and avgNorm located at pNew
5643         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5644         double dot  = ( norms[ iF ] * avgNorm );
5645         if ( dot < std::numeric_limits<double>::min() )
5646           dot = stepSize * 1e-3;
5647         double step = -( norms[ iF ] * pNew + d ) / dot;
5648         pNew += step * avgNorm;
5649       }
5650     }
5651     else
5652     {
5653       pNew += stepSize * avgNorm;
5654     }
5655     p = pNew;
5656
5657     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658     newNodes.push_back( newNode );
5659   }
5660   return nbNodes;
5661 }
5662
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByNormal1D
5665 //purpose  : create nodes for extrusion using normals of edges
5666 //=======================================================================
5667
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5670                      const SMDS_MeshNode*              /*srcNode*/,
5671                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5672                      const bool                        /*makeMediumNodes*/)
5673 {
5674   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5675   return 0;
5676 }
5677
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesAlongTrack
5680 //purpose  : create nodes for extrusion along path
5681 //=======================================================================
5682
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5685                      const SMDS_MeshNode*              srcNode,
5686                      std::list<const SMDS_MeshNode*> & newNodes,
5687                      const bool                        makeMediumNodes)
5688 {
5689   const Standard_Real aTolAng=1.e-4;
5690
5691   gp_Pnt aV0x = myBaseP;
5692   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5693
5694   const PathPoint& aPP0 = myPathPoints[0];
5695   gp_Pnt aP0x = aPP0.myPnt;
5696   gp_Dir aDT0x= aPP0.myTgt;
5697
5698   std::vector< gp_Pnt > centers;
5699   centers.reserve( NbSteps() * 2 );
5700
5701   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5702
5703   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5704   {
5705     const PathPoint&  aPP  = myPathPoints[j];
5706     const gp_Pnt&     aP1x = aPP.myPnt;
5707     const gp_Dir&    aDT1x = aPP.myTgt;
5708
5709     // Translation
5710     gp_Vec aV01x( aP0x, aP1x );
5711     aTrsf.SetTranslation( aV01x );
5712     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5713     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5714
5715     // rotation 1 [ T1,T0 ]
5716     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5717     if ( fabs( aAngleT1T0 ) > aTolAng )
5718     {
5719       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5720       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5721
5722       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5723     }
5724
5725     // rotation 2
5726     if ( aPP.myAngle != 0. )
5727     {
5728       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5729       aPN1 = aPN1.Transformed( aTrsfRot );
5730     }
5731
5732     // make new node
5733     if ( makeMediumNodes )
5734     {
5735       // create additional node
5736       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5737       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5738       newNodes.push_back( newNode );
5739
5740     }
5741     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5742     newNodes.push_back( newNode );
5743
5744     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5745     centers.push_back( aV1x );
5746
5747     aPN0 = aPN1;
5748     aP0x = aP1x;
5749     aV0x = aV1x;
5750     aDT0x = aDT1x;
5751   }
5752
5753   // scale
5754   if ( !myScales.empty() )
5755   {
5756     gp_Trsf aTrsfScale;
5757     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5758     for ( size_t i = !makeMediumNodes;
5759           i < myScales.size() && node != newNodes.end();
5760           i += ( 1 + !makeMediumNodes ), ++node )
5761     {
5762       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5763       gp_Pnt aN = SMESH_NodeXYZ( *node );
5764       gp_Pnt aP = aN.Transformed( aTrsfScale );
5765       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5766     }
5767   }
5768
5769   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5770 }
5771
5772 //=======================================================================
5773 //function : ExtrusionSweep
5774 //purpose  :
5775 //=======================================================================
5776
5777 SMESH_MeshEditor::PGroupIDs
5778 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5779                                   const gp_Vec&        theStep,
5780                                   const int            theNbSteps,
5781                                   TTElemOfElemListMap& newElemsMap,
5782                                   const int            theFlags,
5783                                   const double         theTolerance)
5784 {
5785   std::list<double> dummy;
5786   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5787                        theFlags, theTolerance );
5788   return ExtrusionSweep( theElems, aParams, newElemsMap );
5789 }
5790
5791 namespace
5792 {
5793
5794 //=======================================================================
5795 //function : getOriFactor
5796 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5797 //           edge curve orientation
5798 //=======================================================================
5799
5800   double getOriFactor( const TopoDS_Edge&   edge,
5801                        const SMDS_MeshNode* n1,
5802                        const SMDS_MeshNode* n2,
5803                        SMESH_MesherHelper&  helper)
5804   {
5805     double u1 = helper.GetNodeU( edge, n1, n2 );
5806     double u2 = helper.GetNodeU( edge, n2, n1 );
5807     return u1 < u2 ? 1. : -1.;
5808   }
5809 }
5810
5811 //=======================================================================
5812 //function : ExtrusionSweep
5813 //purpose  :
5814 //=======================================================================
5815
5816 SMESH_MeshEditor::PGroupIDs
5817 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5818                                   ExtrusParam&         theParams,
5819                                   TTElemOfElemListMap& newElemsMap)
5820 {
5821   ClearLastCreated();
5822
5823   setElemsFirst( theElemSets );
5824   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5825   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5826
5827   // source elements for each generated one
5828   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5829   srcElems.reserve( theElemSets[0].size() );
5830   srcNodes.reserve( theElemSets[1].size() );
5831
5832   const int nbSteps = theParams.NbSteps();
5833   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5834
5835   TNodeOfNodeListMap   mapNewNodes;
5836   TElemOfVecOfNnlmiMap mapElemNewNodes;
5837
5838   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5839                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5840                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5841   // loop on theElems
5842   TIDSortedElemSet::iterator itElem;
5843   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5844   {
5845     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5846     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5847     {
5848       // check element type
5849       const SMDS_MeshElement* elem = *itElem;
5850       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5851         continue;
5852
5853       const size_t nbNodes = elem->NbNodes();
5854       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5855       newNodesItVec.reserve( nbNodes );
5856
5857       // loop on elem nodes
5858       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5859       while ( itN->more() )
5860       {
5861         // check if a node has been already sweeped
5862         const SMDS_MeshNode* node = itN->next();
5863         TNodeOfNodeListMap::iterator nIt =
5864           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5865         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5866         if ( listNewNodes.empty() )
5867         {
5868           // make new nodes
5869
5870           // check if we are to create medium nodes between corner ones
5871           bool needMediumNodes = false;
5872           if ( isQuadraticMesh )
5873           {
5874             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5875             while (it->more() && !needMediumNodes )
5876             {
5877               const SMDS_MeshElement* invElem = it->next();
5878               if ( invElem != elem && !theElems.count( invElem )) continue;
5879               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5880               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5881                 needMediumNodes = true;
5882             }
5883           }
5884           // create nodes for all steps
5885           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5886           {
5887             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5888             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5889             {
5890               myLastCreatedNodes.push_back( *newNodesIt );
5891               srcNodes.push_back( node );
5892             }
5893           }
5894           else
5895           {
5896             if ( theParams.ToMakeBoundary() )
5897             {
5898               GetMeshDS()->Modified();
5899               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5900             }
5901             break; // newNodesItVec will be shorter than nbNodes
5902           }
5903         }
5904         newNodesItVec.push_back( nIt );
5905       }
5906       // make new elements
5907       if ( newNodesItVec.size() == nbNodes )
5908         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5909     }
5910   }
5911
5912   if ( theParams.ToMakeBoundary() ) {
5913     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5914   }
5915   PGroupIDs newGroupIDs;
5916   if ( theParams.ToMakeGroups() )
5917     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5918
5919   return newGroupIDs;
5920 }
5921
5922 //=======================================================================
5923 //function : ExtrusionAlongTrack
5924 //purpose  :
5925 //=======================================================================
5926 SMESH_MeshEditor::Extrusion_Error
5927 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5928                                        SMESH_Mesh*          theTrackMesh,
5929                                        SMDS_ElemIteratorPtr theTrackIterator,
5930                                        const SMDS_MeshNode* theN1,
5931                                        std::list<double>&   theAngles,
5932                                        const bool           theAngleVariation,
5933                                        std::list<double>&   theScales,
5934                                        const bool           theScaleVariation,
5935                                        const gp_Pnt*        theRefPoint,
5936                                        const bool           theMakeGroups)
5937 {
5938   ClearLastCreated();
5939
5940   // 1. Check data
5941   if ( theElements[0].empty() && theElements[1].empty() )
5942     return EXTR_NO_ELEMENTS;
5943
5944   ASSERT( theTrackMesh );
5945   if ( ! theTrackIterator || !theTrackIterator->more() )
5946     return EXTR_NO_ELEMENTS;
5947
5948   // 2. Get ordered nodes
5949   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5950   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5951   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5952   if ( branchEdges.empty() )
5953     return EXTR_PATH_NOT_EDGE;
5954
5955   if ( branchEdges.size() > 1 )
5956     return EXTR_BAD_PATH_SHAPE;
5957
5958   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5959   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5960   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5961     return EXTR_BAD_STARTING_NODE;
5962
5963   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5964   {
5965     // add medium nodes to pathNodes
5966     std::vector< const SMDS_MeshNode* >    pathNodes2;
5967     std::vector< const SMDS_MeshElement* > pathEdges2;
5968     pathNodes2.reserve( pathNodes.size() * 2 );
5969     pathEdges2.reserve( pathEdges.size() * 2 );
5970     for ( size_t i = 0; i < pathEdges.size(); ++i )
5971     {
5972       pathNodes2.push_back( pathNodes[i] );
5973       pathEdges2.push_back( pathEdges[i] );
5974       if ( pathEdges[i]->IsQuadratic() )
5975       {
5976         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5977         pathEdges2.push_back( pathEdges[i] );
5978       }
5979     }
5980     pathNodes2.push_back( pathNodes.back() );
5981     pathEdges.swap( pathEdges2 );
5982     pathNodes.swap( pathNodes2 );
5983   }
5984
5985   // 3. Get path data at pathNodes
5986
5987   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5988
5989   if ( theAngleVariation )
5990     linearAngleVariation( points.size()-1, theAngles );
5991   if ( theScaleVariation )
5992     linearScaleVariation( points.size()-1, theScales );
5993
5994   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5995   std::list<double>::iterator angle = theAngles.begin();
5996
5997   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5998
5999   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6000   std::map< int, double >::iterator id2factor;
6001   SMESH_MesherHelper pathHelper( *theTrackMesh );
6002   gp_Pnt p; gp_Vec tangent;
6003   const double tol2 = gp::Resolution() * gp::Resolution();
6004
6005   for ( size_t i = 0; i < pathNodes.size(); ++i )
6006   {
6007     ExtrusParam::PathPoint & point = points[ i ];
6008
6009     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6010
6011     if ( angle != theAngles.end() )
6012       point.myAngle = *angle++;
6013
6014     tangent.SetCoord( 0,0,0 );
6015     const int          shapeID = pathNodes[ i ]->GetShapeID();
6016     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6017     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6018     switch ( shapeType )
6019     {
6020     case TopAbs_EDGE:
6021     {
6022       TopoDS_Edge edge = TopoDS::Edge( shape );
6023       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6024       if ( id2factor->second == 0 )
6025       {
6026         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6027         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6028       }
6029       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6030       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6031       curve->D1( u, p, tangent );
6032       tangent *= id2factor->second;
6033       break;
6034     }
6035     case TopAbs_VERTEX:
6036     {
6037       int nbEdges = 0;
6038       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6039       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6040       {
6041         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6042         for ( int di = -1; di <= 0; ++di )
6043         {
6044           size_t j = i + di;
6045           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6046           {
6047             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6048             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6049             if ( id2factor->second == 0 )
6050             {
6051               if ( j < i )
6052                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6053               else
6054                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6055             }
6056             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6057             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6058             gp_Vec du;
6059             curve->D1( u, p, du );
6060             double size2 = du.SquareMagnitude();
6061             if ( du.SquareMagnitude() > tol2 )
6062             {
6063               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6064               nbEdges++;
6065             }
6066             break;
6067           }
6068         }
6069       }
6070       if ( nbEdges > 0 )
6071         break;
6072     }
6073     // fall through
6074     default:
6075     {
6076       for ( int di = -1; di <= 1; di += 2 )
6077       {
6078         size_t j = i + di;
6079         if ( j < pathNodes.size() )
6080         {
6081           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6082           double size2 = dir.SquareMagnitude();
6083           if ( size2 > tol2 )
6084             tangent += dir.Divided( Sqrt( size2 )) * di;
6085         }
6086       }
6087     }
6088     } // switch ( shapeType )
6089
6090     if ( tangent.SquareMagnitude() < tol2 )
6091       return EXTR_CANT_GET_TANGENT;
6092
6093     point.myTgt = tangent;
6094
6095   } // loop on pathNodes
6096
6097
6098   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6099   TTElemOfElemListMap newElemsMap;
6100
6101   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6102
6103   return EXTR_OK;
6104 }
6105
6106 //=======================================================================
6107 //function : linearAngleVariation
6108 //purpose  : spread values over nbSteps
6109 //=======================================================================
6110
6111 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6112                                             list<double>& Angles)
6113 {
6114   int nbAngles = Angles.size();
6115   if( nbSteps > nbAngles && nbAngles > 0 )
6116   {
6117     vector<double> theAngles(nbAngles);
6118     theAngles.assign( Angles.begin(), Angles.end() );
6119
6120     list<double> res;
6121     double rAn2St = double( nbAngles ) / double( nbSteps );
6122     double angPrev = 0, angle;
6123     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6124     {
6125       double angCur = rAn2St * ( iSt+1 );
6126       double angCurFloor  = floor( angCur );
6127       double angPrevFloor = floor( angPrev );
6128       if ( angPrevFloor == angCurFloor )
6129         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6130       else {
6131         int iP = int( angPrevFloor );
6132         double angPrevCeil = ceil(angPrev);
6133         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6134
6135         int iC = int( angCurFloor );
6136         if ( iC < nbAngles )
6137           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6138
6139         iP = int( angPrevCeil );
6140         while ( iC-- > iP )
6141           angle += theAngles[ iC ];
6142       }
6143       res.push_back(angle);
6144       angPrev = angCur;
6145     }
6146     Angles.swap( res );
6147   }
6148 }
6149
6150 //=======================================================================
6151 //function : linearScaleVariation
6152 //purpose  : spread values over nbSteps 
6153 //=======================================================================
6154
6155 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6156                                             std::list<double>& theScales)
6157 {
6158   int nbScales = theScales.size();
6159   std::vector<double> myScales;
6160   myScales.reserve( theNbSteps );
6161   std::list<double>::const_iterator scale = theScales.begin();
6162   double prevScale = 1.0;
6163   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6164   {
6165     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6166     int    stDelta = Max( 1, iStep - myScales.size());
6167     double scDelta = ( *scale - prevScale ) / stDelta;
6168     for ( int iStep = 0; iStep < stDelta; ++iStep )
6169     {
6170       myScales.push_back( prevScale + scDelta );
6171       prevScale = myScales.back();
6172     }
6173     prevScale = *scale;
6174   }
6175   theScales.assign( myScales.begin(), myScales.end() );
6176 }
6177
6178 //================================================================================
6179 /*!
6180  * \brief Move or copy theElements applying theTrsf to their nodes
6181  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6182  *  \param theTrsf - transformation to apply
6183  *  \param theCopy - if true, create translated copies of theElems
6184  *  \param theMakeGroups - if true and theCopy, create translated groups
6185  *  \param theTargetMesh - mesh to copy translated elements into
6186  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6187  */
6188 //================================================================================
6189
6190 SMESH_MeshEditor::PGroupIDs
6191 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6192                              const gp_Trsf&     theTrsf,
6193                              const bool         theCopy,
6194                              const bool         theMakeGroups,
6195                              SMESH_Mesh*        theTargetMesh)
6196 {
6197   ClearLastCreated();
6198   myLastCreatedElems.reserve( theElems.size() );
6199
6200   bool needReverse = false;
6201   string groupPostfix;
6202   switch ( theTrsf.Form() ) {
6203   case gp_PntMirror:
6204     needReverse = true;
6205     groupPostfix = "mirrored";
6206     break;
6207   case gp_Ax1Mirror:
6208     groupPostfix = "mirrored";
6209     break;
6210   case gp_Ax2Mirror:
6211     needReverse = true;
6212     groupPostfix = "mirrored";
6213     break;
6214   case gp_Rotation:
6215     groupPostfix = "rotated";
6216     break;
6217   case gp_Translation:
6218     groupPostfix = "translated";
6219     break;
6220   case gp_Scale:
6221     groupPostfix = "scaled";
6222     break;
6223   case gp_CompoundTrsf: // different scale by axis
6224     groupPostfix = "scaled";
6225     break;
6226   default:
6227     needReverse = false;
6228     groupPostfix = "transformed";
6229   }
6230
6231   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6232   SMESHDS_Mesh* aMesh    = GetMeshDS();
6233
6234   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6235   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6236   SMESH_MeshEditor::ElemFeatures elemType;
6237
6238   // map old node to new one
6239   TNodeNodeMap nodeMap;
6240
6241   // elements sharing moved nodes; those of them which have all
6242   // nodes mirrored but are not in theElems are to be reversed
6243   TIDSortedElemSet inverseElemSet;
6244
6245   // source elements for each generated one
6246   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6247
6248   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6249   TIDSortedElemSet orphanNode;
6250
6251   if ( theElems.empty() ) // transform the whole mesh
6252   {
6253     // add all elements
6254     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6255     while ( eIt->more() ) theElems.insert( eIt->next() );
6256     // add orphan nodes
6257     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6258     while ( nIt->more() )
6259     {
6260       const SMDS_MeshNode* node = nIt->next();
6261       if ( node->NbInverseElements() == 0)
6262         orphanNode.insert( node );
6263     }
6264   }
6265
6266   // loop on elements to transform nodes : first orphan nodes then elems
6267   TIDSortedElemSet::iterator itElem;
6268   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6269   for (int i=0; i<2; i++)
6270     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6271     {
6272       const SMDS_MeshElement* elem = *itElem;
6273       if ( !elem )
6274         continue;
6275
6276       // loop on elem nodes
6277       double coord[3];
6278       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6279       while ( itN->more() )
6280       {
6281         const SMDS_MeshNode* node = cast2Node( itN->next() );
6282         // check if a node has been already transformed
6283         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6284           nodeMap.insert( make_pair ( node, node ));
6285         if ( !n2n_isnew.second )
6286           continue;
6287
6288         node->GetXYZ( coord );
6289         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6290         if ( theTargetMesh ) {
6291           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6292           n2n_isnew.first->second = newNode;
6293           myLastCreatedNodes.push_back(newNode);
6294           srcNodes.push_back( node );
6295         }
6296         else if ( theCopy ) {
6297           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6298           n2n_isnew.first->second = newNode;
6299           myLastCreatedNodes.push_back(newNode);
6300           srcNodes.push_back( node );
6301         }
6302         else {
6303           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6304           // node position on shape becomes invalid
6305           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6306             ( SMDS_SpacePosition::originSpacePosition() );
6307         }
6308
6309         // keep inverse elements
6310         if ( !theCopy && !theTargetMesh && needReverse ) {
6311           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6312           while ( invElemIt->more() ) {
6313             const SMDS_MeshElement* iel = invElemIt->next();
6314             inverseElemSet.insert( iel );
6315           }
6316         }
6317       }
6318     } // loop on elems in { &orphanNode, &theElems };
6319
6320   // either create new elements or reverse mirrored ones
6321   if ( !theCopy && !needReverse && !theTargetMesh )
6322     return PGroupIDs();
6323
6324   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6325
6326   // Replicate or reverse elements
6327
6328   std::vector<int> iForw;
6329   vector<const SMDS_MeshNode*> nodes;
6330   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6331   {
6332     const SMDS_MeshElement* elem = *itElem;
6333     if ( !elem ) continue;
6334
6335     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6336     size_t               nbNodes  = elem->NbNodes();
6337     if ( geomType == SMDSGeom_NONE ) continue; // node
6338
6339     nodes.resize( nbNodes );
6340
6341     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6342     {
6343       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6344       if ( !aPolyedre )
6345         continue;
6346       nodes.clear();
6347       bool allTransformed = true;
6348       int nbFaces = aPolyedre->NbFaces();
6349       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6350       {
6351         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6352         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6353         {
6354           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6355           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6356           if ( nodeMapIt == nodeMap.end() )
6357             allTransformed = false; // not all nodes transformed
6358           else
6359             nodes.push_back((*nodeMapIt).second);
6360         }
6361         if ( needReverse && allTransformed )
6362           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6363       }
6364       if ( !allTransformed )
6365         continue; // not all nodes transformed
6366     }
6367     else // ----------------------- the rest element types
6368     {
6369       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6370       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6371       const vector<int>&    i = needReverse ? iRev : iForw;
6372
6373       // find transformed nodes
6374       size_t iNode = 0;
6375       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6376       while ( itN->more() ) {
6377         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6378         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6379         if ( nodeMapIt == nodeMap.end() )
6380           break; // not all nodes transformed
6381         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6382       }
6383       if ( iNode != nbNodes )
6384         continue; // not all nodes transformed
6385     }
6386
6387     if ( editor ) {
6388       // copy in this or a new mesh
6389       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6390         srcElems.push_back( elem );
6391     }
6392     else {
6393       // reverse element as it was reversed by transformation
6394       if ( nbNodes > 2 )
6395         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6396     }
6397
6398   } // loop on elements
6399
6400   if ( editor && editor != this )
6401     myLastCreatedElems.swap( editor->myLastCreatedElems );
6402
6403   PGroupIDs newGroupIDs;
6404
6405   if ( ( theMakeGroups && theCopy ) ||
6406        ( theMakeGroups && theTargetMesh ) )
6407     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6408
6409   return newGroupIDs;
6410 }
6411
6412 //================================================================================
6413 /*!
6414  * \brief Make an offset mesh from a source 2D mesh
6415  *  \param [in] theElements - source faces
6416  *  \param [in] theValue - offset value
6417  *  \param [out] theTgtMesh - a mesh to add offset elements to
6418  *  \param [in] theMakeGroups - to generate groups
6419  *  \return PGroupIDs - IDs of created groups. NULL means failure
6420  */
6421 //================================================================================
6422
6423 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6424                                                       const double       theValue,
6425                                                       SMESH_Mesh*        theTgtMesh,
6426                                                       const bool         theMakeGroups,
6427                                                       const bool         theCopyElements,
6428                                                       const bool         theFixSelfIntersection)
6429 {
6430   SMESHDS_Mesh*    meshDS = GetMeshDS();
6431   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6432   SMESH_MeshEditor tgtEditor( theTgtMesh );
6433
6434   SMDS_ElemIteratorPtr eIt;
6435   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6436   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6437
6438   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6439   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6440   std::unique_ptr< SMDS_Mesh > offsetMesh
6441     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6442                                    theFixSelfIntersection,
6443                                    new2OldFaces, new2OldNodes ));
6444   if ( offsetMesh->NbElements() == 0 )
6445     return PGroupIDs(); // MakeOffset() failed
6446
6447
6448   if ( theTgtMesh == myMesh && !theCopyElements )
6449   {
6450     // clear the source elements
6451     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6452     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6453     while ( eIt->more() )
6454       meshDS->RemoveFreeElement( eIt->next(), 0 );
6455   }
6456
6457   // offsetMesh->Modified();
6458   // offsetMesh->CompactMesh(); // make IDs start from 1
6459
6460   // source elements for each generated one
6461   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6462   srcElems.reserve( new2OldFaces.size() );
6463   srcNodes.reserve( new2OldNodes.size() );
6464
6465   ClearLastCreated();
6466   myLastCreatedElems.reserve( new2OldFaces.size() );
6467   myLastCreatedNodes.reserve( new2OldNodes.size() );
6468
6469   // copy offsetMesh to theTgtMesh
6470
6471   int idShift = meshDS->MaxNodeID();
6472   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6473     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6474     {
6475 #ifndef _DEBUG_
6476       if ( n->NbInverseElements() > 0 )
6477 #endif
6478       {
6479         const SMDS_MeshNode* n2 =
6480           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6481         myLastCreatedNodes.push_back( n2 );
6482         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6483       }
6484     }
6485
6486   ElemFeatures elemType;
6487   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6488     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6489     {
6490       elemType.Init( f );
6491       elemType.myNodes.clear();
6492       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6493       {
6494         const SMDS_MeshNode* n2 = nIt->next();
6495         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6496       }
6497       tgtEditor.AddElement( elemType.myNodes, elemType );
6498       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6499     }
6500
6501   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6502
6503   PGroupIDs newGroupIDs;
6504   if ( theMakeGroups )
6505     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6506   else
6507     newGroupIDs.reset( new std::list< int > );
6508
6509   return newGroupIDs;
6510 }
6511
6512 //=======================================================================
6513 /*!
6514  * \brief Create groups of elements made during transformation
6515  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6516  *  \param elemGens - elements making corresponding myLastCreatedElems
6517  *  \param postfix - to push_back to names of new groups
6518  *  \param targetMesh - mesh to create groups in
6519  *  \param topPresent - is there are "top" elements that are created by sweeping
6520  */
6521 //=======================================================================
6522
6523 SMESH_MeshEditor::PGroupIDs
6524 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6525                                  const SMESH_SequenceOfElemPtr& elemGens,
6526                                  const std::string&             postfix,
6527                                  SMESH_Mesh*                    targetMesh,
6528                                  const bool                     topPresent)
6529 {
6530   PGroupIDs newGroupIDs( new list<int> );
6531   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6532
6533   // Sort existing groups by types and collect their names
6534
6535   // containers to store an old group and generated new ones;
6536   // 1st new group is for result elems of different type than a source one;
6537   // 2nd new group is for same type result elems ("top" group at extrusion)
6538   using boost::tuple;
6539   using boost::make_tuple;
6540   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6541   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6542   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6543   // group names
6544   set< string > groupNames;
6545
6546   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6547   if ( !groupIt->more() ) return newGroupIDs;
6548
6549   int newGroupID = mesh->GetGroupIds().back()+1;
6550   while ( groupIt->more() )
6551   {
6552     SMESH_Group * group = groupIt->next();
6553     if ( !group ) continue;
6554     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6555     if ( !groupDS || groupDS->IsEmpty() ) continue;
6556     groupNames.insert    ( group->GetName() );
6557     groupDS->SetStoreName( group->GetName() );
6558     const SMDSAbs_ElementType type = groupDS->GetType();
6559     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6560     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6561     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6562     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6563   }
6564
6565   // Loop on nodes and elements to add them in new groups
6566
6567   vector< const SMDS_MeshElement* > resultElems;
6568   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6569   {
6570     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6571     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6572     if ( gens.size() != elems.size() )
6573       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6574
6575     // loop on created elements
6576     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6577     {
6578       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6579       if ( !sourceElem ) {
6580         MESSAGE("generateGroups(): NULL source element");
6581         continue;
6582       }
6583       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6584       if ( groupsOldNew.empty() ) { // no groups of this type at all
6585         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6586           ++iElem; // skip all elements made by sourceElem
6587         continue;
6588       }
6589       // collect all elements made by the iElem-th sourceElem
6590       resultElems.clear();
6591       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6592         if ( resElem != sourceElem )
6593           resultElems.push_back( resElem );
6594       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6595         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6596           if ( resElem != sourceElem )
6597             resultElems.push_back( resElem );
6598
6599       const SMDS_MeshElement* topElem = 0;
6600       if ( isNodes ) // there must be a top element
6601       {
6602         topElem = resultElems.back();
6603         resultElems.pop_back();
6604       }
6605       else
6606       {
6607         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6608         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6609           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6610           {
6611             topElem = *resElemIt;
6612             *resElemIt = 0; // erase *resElemIt
6613             break;
6614           }
6615       }
6616       // add resultElems to groups originted from ones the sourceElem belongs to
6617       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6618       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6619       {
6620         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6621         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6622         {
6623           // fill in a new group
6624           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6625           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6626           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6627             if ( *resElemIt )
6628               newGroup.Add( *resElemIt );
6629
6630           // fill a "top" group
6631           if ( topElem )
6632           {
6633             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6634             newTopGroup.Add( topElem );
6635           }
6636         }
6637       }
6638     } // loop on created elements
6639   }// loop on nodes and elements
6640
6641   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6642
6643   list<int> topGrouIds;
6644   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6645   {
6646     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6647     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6648                                       orderedOldNewGroups[i]->get<2>() };
6649     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6650     {
6651       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6652       if ( newGroupDS->IsEmpty() )
6653       {
6654         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6655       }
6656       else
6657       {
6658         // set group type
6659         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6660
6661         // make a name
6662         const bool isTop = ( topPresent &&
6663                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6664                              is2nd );
6665
6666         string name = oldGroupDS->GetStoreName();
6667         { // remove trailing whitespaces (issue 22599)
6668           size_t size = name.size();
6669           while ( size > 1 && isspace( name[ size-1 ]))
6670             --size;
6671           if ( size != name.size() )
6672           {
6673             name.resize( size );
6674             oldGroupDS->SetStoreName( name.c_str() );
6675           }
6676         }
6677         if ( !targetMesh ) {
6678           string suffix = ( isTop ? "top": postfix.c_str() );
6679           name += "_";
6680           name += suffix;
6681           int nb = 1;
6682           while ( !groupNames.insert( name ).second ) // name exists
6683             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6684         }
6685         else if ( isTop ) {
6686           name += "_top";
6687         }
6688         newGroupDS->SetStoreName( name.c_str() );
6689
6690         // make a SMESH_Groups
6691         mesh->AddGroup( newGroupDS );
6692         if ( isTop )
6693           topGrouIds.push_back( newGroupDS->GetID() );
6694         else
6695           newGroupIDs->push_back( newGroupDS->GetID() );
6696       }
6697     }
6698   }
6699   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6700
6701   return newGroupIDs;
6702 }
6703
6704 //================================================================================
6705 /*!
6706  *  * \brief Return list of group of nodes close to each other within theTolerance
6707  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6708  *  *        an Octree algorithm
6709  *  \param [in,out] theNodes - the nodes to treat
6710  *  \param [in]     theTolerance - the tolerance
6711  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6712  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6713  *         corner and medium nodes in separate groups
6714  */
6715 //================================================================================
6716
6717 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6718                                             const double         theTolerance,
6719                                             TListOfListOfNodes & theGroupsOfNodes,
6720                                             bool                 theSeparateCornersAndMedium)
6721 {
6722   ClearLastCreated();
6723
6724   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6725        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6726        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6727     theSeparateCornersAndMedium = false;
6728
6729   TIDSortedNodeSet& corners = theNodes;
6730   TIDSortedNodeSet  medium;
6731
6732   if ( theNodes.empty() ) // get all nodes in the mesh
6733   {
6734     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6735     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6736     if ( theSeparateCornersAndMedium )
6737       while ( nIt->more() )
6738       {
6739         const SMDS_MeshNode* n = nIt->next();
6740         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6741         nodeSet->insert( nodeSet->end(), n );
6742       }
6743     else
6744       while ( nIt->more() )
6745         theNodes.insert( theNodes.end(), nIt->next() );
6746   }
6747   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6748   {
6749     TIDSortedNodeSet::iterator nIt = corners.begin();
6750     while ( nIt != corners.end() )
6751       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6752       {
6753         medium.insert( medium.end(), *nIt );
6754         corners.erase( nIt++ );
6755       }
6756       else
6757       {
6758         ++nIt;
6759       }
6760   }
6761
6762   if ( !corners.empty() )
6763     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6764   if ( !medium.empty() )
6765     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6766 }
6767
6768 //=======================================================================
6769 //function : SimplifyFace
6770 //purpose  : split a chain of nodes into several closed chains
6771 //=======================================================================
6772
6773 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6774                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6775                                     vector<int>&                         quantities) const
6776 {
6777   int nbNodes = faceNodes.size();
6778   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6779     --nbNodes;
6780   if ( nbNodes < 3 )
6781     return 0;
6782   size_t prevNbQuant = quantities.size();
6783
6784   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6785   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6786   map< const SMDS_MeshNode*, int >::iterator nInd;
6787
6788   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6789   simpleNodes.push_back( faceNodes[0] );
6790   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6791   {
6792     if ( faceNodes[ iCur ] != simpleNodes.back() )
6793     {
6794       int index = simpleNodes.size();
6795       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6796       int prevIndex = nInd->second;
6797       if ( prevIndex < index )
6798       {
6799         // a sub-loop found
6800         int loopLen = index - prevIndex;
6801         if ( loopLen > 2 )
6802         {
6803           // store the sub-loop
6804           quantities.push_back( loopLen );
6805           for ( int i = prevIndex; i < index; i++ )
6806             poly_nodes.push_back( simpleNodes[ i ]);
6807         }
6808         simpleNodes.resize( prevIndex+1 );
6809       }
6810       else
6811       {
6812         simpleNodes.push_back( faceNodes[ iCur ]);
6813       }
6814     }
6815   }
6816
6817   if ( simpleNodes.size() > 2 )
6818   {
6819     quantities.push_back( simpleNodes.size() );
6820     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6821   }
6822
6823   return quantities.size() - prevNbQuant;
6824 }
6825
6826 //=======================================================================
6827 //function : MergeNodes
6828 //purpose  : In each group, the cdr of nodes are substituted by the first one
6829 //           in all elements.
6830 //=======================================================================
6831
6832 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6833                                    const bool           theAvoidMakingHoles)
6834 {
6835   ClearLastCreated();
6836
6837   SMESHDS_Mesh* mesh = GetMeshDS();
6838
6839   TNodeNodeMap nodeNodeMap; // node to replace - new node
6840   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6841   list< int > rmElemIds, rmNodeIds;
6842   vector< ElemFeatures > newElemDefs;
6843
6844   // Fill nodeNodeMap and elems
6845
6846   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6847   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6848   {
6849     list<const SMDS_MeshNode*>& nodes = *grIt;
6850     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6851     const SMDS_MeshNode* nToKeep = *nIt;
6852     for ( ++nIt; nIt != nodes.end(); nIt++ )
6853     {
6854       const SMDS_MeshNode* nToRemove = *nIt;
6855       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6856       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6857       while ( invElemIt->more() ) {
6858         const SMDS_MeshElement* elem = invElemIt->next();
6859         elems.insert(elem);
6860       }
6861     }
6862   }
6863
6864   // Apply recursive replacements (BUG 0020185)
6865   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6866   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6867   {
6868     const SMDS_MeshNode* nToKeep = nnIt->second;
6869     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6870     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6871     {
6872       nToKeep = nnIt_i->second;
6873       nnIt->second = nToKeep;
6874       nnIt_i = nodeNodeMap.find( nToKeep );
6875     }
6876   }
6877
6878   if ( theAvoidMakingHoles )
6879   {
6880     // find elements whose topology changes
6881
6882     vector<const SMDS_MeshElement*> pbElems;
6883     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6884     for ( ; eIt != elems.end(); ++eIt )
6885     {
6886       const SMDS_MeshElement* elem = *eIt;
6887       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6888       while ( itN->more() )
6889       {
6890         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6891         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6892         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6893         {
6894           // several nodes of elem stick
6895           pbElems.push_back( elem );
6896           break;
6897         }
6898       }
6899     }
6900     // exclude from merge nodes causing spoiling element
6901     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6902     {
6903       bool nodesExcluded = false;
6904       for ( size_t i = 0; i < pbElems.size(); ++i )
6905       {
6906         size_t prevNbMergeNodes = nodeNodeMap.size();
6907         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6908              prevNbMergeNodes < nodeNodeMap.size() )
6909           nodesExcluded = true;
6910       }
6911       if ( !nodesExcluded )
6912         break;
6913     }
6914   }
6915
6916   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6917   {
6918     const SMDS_MeshNode* nToRemove = nnIt->first;
6919     const SMDS_MeshNode* nToKeep   = nnIt->second;
6920     if ( nToRemove != nToKeep )
6921     {
6922       rmNodeIds.push_back( nToRemove->GetID() );
6923       AddToSameGroups( nToKeep, nToRemove, mesh );
6924       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6925       // w/o creating node in place of merged ones.
6926       SMDS_PositionPtr pos = nToRemove->GetPosition();
6927       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6928         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6929           sm->SetIsAlwaysComputed( true );
6930     }
6931   }
6932
6933   // Change element nodes or remove an element
6934
6935   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6936   for ( ; eIt != elems.end(); eIt++ )
6937   {
6938     const SMDS_MeshElement* elem = *eIt;
6939     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6940
6941     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6942     if ( !keepElem )
6943       rmElemIds.push_back( elem->GetID() );
6944
6945     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6946     {
6947       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6948                                                & newElemDefs[i].myNodes[0],
6949                                                newElemDefs[i].myNodes.size() ))
6950       {
6951         if ( i == 0 )
6952         {
6953           newElemDefs[i].SetID( elem->GetID() );
6954           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6955           if ( !keepElem ) rmElemIds.pop_back();
6956         }
6957         else
6958         {
6959           newElemDefs[i].SetID( -1 );
6960         }
6961         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6962         if ( sm && newElem )
6963           sm->AddElement( newElem );
6964         if ( elem != newElem )
6965           ReplaceElemInGroups( elem, newElem, mesh );
6966       }
6967     }
6968   }
6969
6970   // Remove bad elements, then equal nodes (order important)
6971   Remove( rmElemIds, /*isNodes=*/false );
6972   Remove( rmNodeIds, /*isNodes=*/true );
6973
6974   return;
6975 }
6976
6977 //=======================================================================
6978 //function : applyMerge
6979 //purpose  : Compute new connectivity of an element after merging nodes
6980 //  \param [in] elems - the element
6981 //  \param [out] newElemDefs - definition(s) of result element(s)
6982 //  \param [inout] nodeNodeMap - nodes to merge
6983 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6984 //              after merging (but not degenerated), removes nodes causing
6985 //              the invalidity from \a nodeNodeMap.
6986 //  \return bool - true if the element should be removed
6987 //=======================================================================
6988
6989 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6990                                    vector< ElemFeatures >& newElemDefs,
6991                                    TNodeNodeMap&           nodeNodeMap,
6992                                    const bool              avoidMakingHoles )
6993 {
6994   bool toRemove = false; // to remove elem
6995   int nbResElems = 1;    // nb new elements
6996
6997   newElemDefs.resize(nbResElems);
6998   newElemDefs[0].Init( elem );
6999   newElemDefs[0].myNodes.clear();
7000
7001   set<const SMDS_MeshNode*> nodeSet;
7002   vector< const SMDS_MeshNode*>   curNodes;
7003   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7004   vector<int> iRepl;
7005
7006   const        int  nbNodes = elem->NbNodes();
7007   SMDSAbs_EntityType entity = elem->GetEntityType();
7008
7009   curNodes.resize( nbNodes );
7010   uniqueNodes.resize( nbNodes );
7011   iRepl.resize( nbNodes );
7012   int iUnique = 0, iCur = 0, nbRepl = 0;
7013
7014   // Get new seq of nodes
7015
7016   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7017   while ( itN->more() )
7018   {
7019     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7020
7021     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7022     if ( nnIt != nodeNodeMap.end() ) {
7023       n = (*nnIt).second;
7024     }
7025     curNodes[ iCur ] = n;
7026     bool isUnique = nodeSet.insert( n ).second;
7027     if ( isUnique )
7028       uniqueNodes[ iUnique++ ] = n;
7029     else
7030       iRepl[ nbRepl++ ] = iCur;
7031     iCur++;
7032   }
7033
7034   // Analyse element topology after replacement
7035
7036   int nbUniqueNodes = nodeSet.size();
7037   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7038   {
7039     toRemove = true;
7040     nbResElems = 0;
7041
7042     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7043     {
7044       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7045       int nbCorners = nbNodes / 2;
7046       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7047       {
7048         int iNext = ( iCur + 1 ) % nbCorners;
7049         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7050         {
7051           int iMedium = iCur + nbCorners;
7052           vector< const SMDS_MeshNode* >::iterator i =
7053             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7054                        uniqueNodes.end(),
7055                        curNodes[ iMedium ]);
7056           if ( i != uniqueNodes.end() )
7057           {
7058             --nbUniqueNodes;
7059             for ( ; i+1 != uniqueNodes.end(); ++i )
7060               *i = *(i+1);
7061           }
7062         }
7063       }
7064     }
7065
7066     switch ( entity )
7067     {
7068     case SMDSEntity_Polygon:
7069     case SMDSEntity_Quad_Polygon: // Polygon
7070     {
7071       ElemFeatures* elemType = & newElemDefs[0];
7072       const bool isQuad = elemType->myIsQuad;
7073       if ( isQuad )
7074         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7075           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7076
7077       // a polygon can divide into several elements
7078       vector<const SMDS_MeshNode *> polygons_nodes;
7079       vector<int> quantities;
7080       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7081       newElemDefs.resize( nbResElems );
7082       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7083       {
7084         ElemFeatures* elemType = & newElemDefs[iface];
7085         if ( iface ) elemType->Init( elem );
7086
7087         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7088         int nbNewNodes = quantities[iface];
7089         face_nodes.assign( polygons_nodes.begin() + inode,
7090                            polygons_nodes.begin() + inode + nbNewNodes );
7091         inode += nbNewNodes;
7092         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7093         {
7094           bool isValid = ( nbNewNodes % 2 == 0 );
7095           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7096             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7097           elemType->SetQuad( isValid );
7098           if ( isValid ) // put medium nodes after corners
7099             SMDS_MeshCell::applyInterlaceRev
7100               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7101                                                     nbNewNodes ), face_nodes );
7102         }
7103         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7104       }
7105       nbUniqueNodes = newElemDefs[0].myNodes.size();
7106       break;
7107     } // Polygon
7108
7109     case SMDSEntity_Polyhedra: // Polyhedral volume
7110     {
7111       if ( nbUniqueNodes >= 4 )
7112       {
7113         // each face has to be analyzed in order to check volume validity
7114         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7115         {
7116           int nbFaces = aPolyedre->NbFaces();
7117
7118           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7119           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7120           vector<const SMDS_MeshNode *>  faceNodes;
7121           poly_nodes.clear();
7122           quantities.clear();
7123
7124           for (int iface = 1; iface <= nbFaces; iface++)
7125           {
7126             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7127             faceNodes.resize( nbFaceNodes );
7128             for (int inode = 1; inode <= nbFaceNodes; inode++)
7129             {
7130               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7131               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7132               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7133                 faceNode = (*nnIt).second;
7134               faceNodes[inode - 1] = faceNode;
7135             }
7136             SimplifyFace(faceNodes, poly_nodes, quantities);
7137           }
7138
7139           if ( quantities.size() > 3 )
7140           {
7141             // TODO: remove coincident faces
7142             nbResElems = 1;
7143             nbUniqueNodes = newElemDefs[0].myNodes.size();
7144           }
7145         }
7146       }
7147     }
7148     break;
7149
7150     // Regular elements
7151     // TODO not all the possible cases are solved. Find something more generic?
7152     case SMDSEntity_Edge: //////// EDGE
7153     case SMDSEntity_Triangle: //// TRIANGLE
7154     case SMDSEntity_Quad_Triangle:
7155     case SMDSEntity_Tetra:
7156     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7157     {
7158       break;
7159     }
7160     case SMDSEntity_Quad_Edge:
7161     {
7162       break;
7163     }
7164     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7165     {
7166       if ( nbUniqueNodes < 3 )
7167         toRemove = true;
7168       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7169         toRemove = true; // opposite nodes stick
7170       else
7171         toRemove = false;
7172       break;
7173     }
7174     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7175     {
7176       //   1    5    2
7177       //    +---+---+
7178       //    |       |
7179       //   4+       +6
7180       //    |       |
7181       //    +---+---+
7182       //   0    7    3
7183       if ( nbUniqueNodes == 6 &&
7184            iRepl[0] < 4       &&
7185            ( nbRepl == 1 || iRepl[1] >= 4 ))
7186       {
7187         toRemove = false;
7188       }
7189       break;
7190     }
7191     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7192     {
7193       //   1    5    2
7194       //    +---+---+
7195       //    |       |
7196       //   4+  8+   +6
7197       //    |       |
7198       //    +---+---+
7199       //   0    7    3
7200       if ( nbUniqueNodes == 7 &&
7201            iRepl[0] < 4       &&
7202            ( nbRepl == 1 || iRepl[1] != 8 ))
7203       {
7204         toRemove = false;
7205       }
7206       break;
7207     }
7208     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7209     {
7210       if ( nbUniqueNodes == 4 ) {
7211         // ---------------------------------> tetrahedron
7212         if ( curNodes[3] == curNodes[4] &&
7213              curNodes[3] == curNodes[5] ) {
7214           // top nodes stick
7215           toRemove = false;
7216         }
7217         else if ( curNodes[0] == curNodes[1] &&
7218                   curNodes[0] == curNodes[2] ) {
7219           // bottom nodes stick: set a top before
7220           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7221           uniqueNodes[ 0 ] = curNodes [ 5 ];
7222           uniqueNodes[ 1 ] = curNodes [ 4 ];
7223           uniqueNodes[ 2 ] = curNodes [ 3 ];
7224           toRemove = false;
7225         }
7226         else if (( curNodes[0] == curNodes[3] ) +
7227                  ( curNodes[1] == curNodes[4] ) +
7228                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7229           // a lateral face turns into a line
7230           toRemove = false;
7231         }
7232       }
7233       else if ( nbUniqueNodes == 5 ) {
7234         // PENTAHEDRON --------------------> pyramid
7235         if ( curNodes[0] == curNodes[3] )
7236         {
7237           uniqueNodes[ 0 ] = curNodes[ 1 ];
7238           uniqueNodes[ 1 ] = curNodes[ 4 ];
7239           uniqueNodes[ 2 ] = curNodes[ 5 ];
7240           uniqueNodes[ 3 ] = curNodes[ 2 ];
7241           uniqueNodes[ 4 ] = curNodes[ 0 ];
7242           toRemove = false;
7243         }
7244         if ( curNodes[1] == curNodes[4] )
7245         {
7246           uniqueNodes[ 0 ] = curNodes[ 0 ];
7247           uniqueNodes[ 1 ] = curNodes[ 2 ];
7248           uniqueNodes[ 2 ] = curNodes[ 5 ];
7249           uniqueNodes[ 3 ] = curNodes[ 3 ];
7250           uniqueNodes[ 4 ] = curNodes[ 1 ];
7251           toRemove = false;
7252         }
7253         if ( curNodes[2] == curNodes[5] )
7254         {
7255           uniqueNodes[ 0 ] = curNodes[ 0 ];
7256           uniqueNodes[ 1 ] = curNodes[ 3 ];
7257           uniqueNodes[ 2 ] = curNodes[ 4 ];
7258           uniqueNodes[ 3 ] = curNodes[ 1 ];
7259           uniqueNodes[ 4 ] = curNodes[ 2 ];
7260           toRemove = false;
7261         }
7262       }
7263       break;
7264     }
7265     case SMDSEntity_Hexa:
7266     {
7267       //////////////////////////////////// HEXAHEDRON
7268       SMDS_VolumeTool hexa (elem);
7269       hexa.SetExternalNormal();
7270       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7271         //////////////////////// HEX ---> tetrahedron
7272         for ( int iFace = 0; iFace < 6; iFace++ ) {
7273           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7274           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7275               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7276               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7277             // one face turns into a point ...
7278             int  pickInd = ind[ 0 ];
7279             int iOppFace = hexa.GetOppFaceIndex( iFace );
7280             ind = hexa.GetFaceNodesIndices( iOppFace );
7281             int nbStick = 0;
7282             uniqueNodes.clear();
7283             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7284               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7285                 nbStick++;
7286               else
7287                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7288             }
7289             if ( nbStick == 1 ) {
7290               // ... and the opposite one - into a triangle.
7291               // set a top node
7292               uniqueNodes.push_back( curNodes[ pickInd ]);
7293               toRemove = false;
7294             }
7295             break;
7296           }
7297         }
7298       }
7299       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7300         //////////////////////// HEX ---> prism
7301         int nbTria = 0, iTria[3];
7302         const int *ind; // indices of face nodes
7303         // look for triangular faces
7304         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7305           ind = hexa.GetFaceNodesIndices( iFace );
7306           TIDSortedNodeSet faceNodes;
7307           for ( iCur = 0; iCur < 4; iCur++ )
7308             faceNodes.insert( curNodes[ind[iCur]] );
7309           if ( faceNodes.size() == 3 )
7310             iTria[ nbTria++ ] = iFace;
7311         }
7312         // check if triangles are opposite
7313         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7314         {
7315           // set nodes of the bottom triangle
7316           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7317           vector<int> indB;
7318           for ( iCur = 0; iCur < 4; iCur++ )
7319             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7320               indB.push_back( ind[iCur] );
7321           if ( !hexa.IsForward() )
7322             std::swap( indB[0], indB[2] );
7323           for ( iCur = 0; iCur < 3; iCur++ )
7324             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7325           // set nodes of the top triangle
7326           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7327           for ( iCur = 0; iCur < 3; ++iCur )
7328             for ( int j = 0; j < 4; ++j )
7329               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7330               {
7331                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7332                 break;
7333               }
7334           toRemove = false;
7335           break;
7336         }
7337       }
7338       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7339         //////////////////// HEXAHEDRON ---> pyramid
7340         for ( int iFace = 0; iFace < 6; iFace++ ) {
7341           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7342           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7343               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7344               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7345             // one face turns into a point ...
7346             int iOppFace = hexa.GetOppFaceIndex( iFace );
7347             ind = hexa.GetFaceNodesIndices( iOppFace );
7348             uniqueNodes.clear();
7349             for ( iCur = 0; iCur < 4; iCur++ ) {
7350               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7351                 break;
7352               else
7353                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7354             }
7355             if ( uniqueNodes.size() == 4 ) {
7356               // ... and the opposite one is a quadrangle
7357               // set a top node
7358               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7359               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7360               toRemove = false;
7361             }
7362             break;
7363           }
7364         }
7365       }
7366
7367       if ( toRemove && nbUniqueNodes > 4 ) {
7368         ////////////////// HEXAHEDRON ---> polyhedron
7369         hexa.SetExternalNormal();
7370         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7371         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7372         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7373         quantities.reserve( 6 );     quantities.clear();
7374         for ( int iFace = 0; iFace < 6; iFace++ )
7375         {
7376           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7377           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7378                curNodes[ind[1]] == curNodes[ind[3]] )
7379           {
7380             quantities.clear();
7381             break; // opposite nodes stick
7382           }
7383           nodeSet.clear();
7384           for ( iCur = 0; iCur < 4; iCur++ )
7385           {
7386             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7387               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7388           }
7389           if ( nodeSet.size() < 3 )
7390             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7391           else
7392             quantities.push_back( nodeSet.size() );
7393         }
7394         if ( quantities.size() >= 4 )
7395         {
7396           nbResElems = 1;
7397           nbUniqueNodes = poly_nodes.size();
7398           newElemDefs[0].SetPoly(true);
7399         }
7400       }
7401       break;
7402     } // case HEXAHEDRON
7403
7404     default:
7405       toRemove = true;
7406
7407     } // switch ( entity )
7408
7409     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7410     {
7411       // erase from nodeNodeMap nodes whose merge spoils elem
7412       vector< const SMDS_MeshNode* > noMergeNodes;
7413       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7414       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7415         nodeNodeMap.erase( noMergeNodes[i] );
7416     }
7417     
7418   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7419
7420   uniqueNodes.resize( nbUniqueNodes );
7421
7422   if ( !toRemove && nbResElems == 0 )
7423     nbResElems = 1;
7424
7425   newElemDefs.resize( nbResElems );
7426
7427   return !toRemove;
7428 }
7429
7430
7431 // ========================================================
7432 // class   : ComparableElement
7433 // purpose : allow comparing elements basing on their nodes
7434 // ========================================================
7435
7436 class ComparableElement : public boost::container::flat_set< int >
7437 {
7438   typedef boost::container::flat_set< int >  int_set;
7439
7440   const SMDS_MeshElement* myElem;
7441   int                     mySumID;
7442   mutable int             myGroupID;
7443
7444 public:
7445
7446   ComparableElement( const SMDS_MeshElement* theElem ):
7447     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7448   {
7449     this->reserve( theElem->NbNodes() );
7450     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7451     {
7452       int id = nodeIt->next()->GetID();
7453       mySumID += id;
7454       this->insert( id );
7455     }
7456   }
7457
7458   const SMDS_MeshElement* GetElem() const { return myElem; }
7459
7460   int& GroupID() const { return myGroupID; }
7461   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7462
7463   ComparableElement( const ComparableElement& theSource ) // move copy
7464     : int_set()
7465   {
7466     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7467     (int_set&) (*this ) = std::move( src );
7468     myElem    = src.myElem;
7469     mySumID   = src.mySumID;
7470     myGroupID = src.myGroupID;
7471   }
7472
7473   static int HashCode(const ComparableElement& se, int limit )
7474   {
7475     return ::HashCode( se.mySumID, limit );
7476   }
7477   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7478   {
7479     return ( se1 == se2 );
7480   }
7481
7482 };
7483
7484 //=======================================================================
7485 //function : FindEqualElements
7486 //purpose  : Return list of group of elements built on the same nodes.
7487 //           Search among theElements or in the whole mesh if theElements is empty
7488 //=======================================================================
7489
7490 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7491                                           TListOfListOfElementsID & theGroupsOfElementsID )
7492 {
7493   ClearLastCreated();
7494
7495   SMDS_ElemIteratorPtr elemIt;
7496   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7497   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7498
7499   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7500   typedef std::list<int>                                          TGroupOfElems;
7501   TMapOfElements               mapOfElements;
7502   std::vector< TGroupOfElems > arrayOfGroups;
7503   TGroupOfElems                groupOfElems;
7504
7505   while ( elemIt->more() )
7506   {
7507     const SMDS_MeshElement* curElem = elemIt->next();
7508     if ( curElem->IsNull() )
7509       continue;
7510     ComparableElement      compElem = curElem;
7511     // check uniqueness
7512     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7513     if ( elemInSet.GetElem() != curElem ) // coincident elem
7514     {
7515       int& iG = elemInSet.GroupID();
7516       if ( iG < 0 )
7517       {
7518         iG = arrayOfGroups.size();
7519         arrayOfGroups.push_back( groupOfElems );
7520         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7521       }
7522       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7523     }
7524   }
7525
7526   groupOfElems.clear();
7527   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7528   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7529   {
7530     if ( groupIt->size() > 1 ) {
7531       //groupOfElems.sort(); -- theElements are sorted already
7532       theGroupsOfElementsID.emplace_back( *groupIt );
7533     }
7534   }
7535 }
7536
7537 //=======================================================================
7538 //function : MergeElements
7539 //purpose  : In each given group, substitute all elements by the first one.
7540 //=======================================================================
7541
7542 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7543 {
7544   ClearLastCreated();
7545
7546   typedef list<int> TListOfIDs;
7547   TListOfIDs rmElemIds; // IDs of elems to remove
7548
7549   SMESHDS_Mesh* aMesh = GetMeshDS();
7550
7551   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7552   while ( groupsIt != theGroupsOfElementsID.end() ) {
7553     TListOfIDs& aGroupOfElemID = *groupsIt;
7554     aGroupOfElemID.sort();
7555     int elemIDToKeep = aGroupOfElemID.front();
7556     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7557     aGroupOfElemID.pop_front();
7558     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7559     while ( idIt != aGroupOfElemID.end() ) {
7560       int elemIDToRemove = *idIt;
7561       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7562       // add the kept element in groups of removed one (PAL15188)
7563       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7564       rmElemIds.push_back( elemIDToRemove );
7565       ++idIt;
7566     }
7567     ++groupsIt;
7568   }
7569
7570   Remove( rmElemIds, false );
7571 }
7572
7573 //=======================================================================
7574 //function : MergeEqualElements
7575 //purpose  : Remove all but one of elements built on the same nodes.
7576 //=======================================================================
7577
7578 void SMESH_MeshEditor::MergeEqualElements()
7579 {
7580   TIDSortedElemSet aMeshElements; /* empty input ==
7581                                      to merge equal elements in the whole mesh */
7582   TListOfListOfElementsID aGroupsOfElementsID;
7583   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7584   MergeElements( aGroupsOfElementsID );
7585 }
7586
7587 //=======================================================================
7588 //function : findAdjacentFace
7589 //purpose  :
7590 //=======================================================================
7591
7592 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7593                                                 const SMDS_MeshNode* n2,
7594                                                 const SMDS_MeshElement* elem)
7595 {
7596   TIDSortedElemSet elemSet, avoidSet;
7597   if ( elem )
7598     avoidSet.insert ( elem );
7599   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7600 }
7601
7602 //=======================================================================
7603 //function : findSegment
7604 //purpose  : Return a mesh segment by two nodes one of which can be medium
7605 //=======================================================================
7606
7607 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7608                                            const SMDS_MeshNode* n2)
7609 {
7610   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7611   while ( it->more() )
7612   {
7613     const SMDS_MeshElement* seg = it->next();
7614     if ( seg->GetNodeIndex( n2 ) >= 0 )
7615       return seg;
7616   }
7617   return 0;
7618 }
7619
7620 //=======================================================================
7621 //function : FindFreeBorder
7622 //purpose  :
7623 //=======================================================================
7624
7625 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7626
7627 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7628                                        const SMDS_MeshNode*             theSecondNode,
7629                                        const SMDS_MeshNode*             theLastNode,
7630                                        list< const SMDS_MeshNode* > &   theNodes,
7631                                        list< const SMDS_MeshElement* >& theFaces)
7632 {
7633   if ( !theFirstNode || !theSecondNode )
7634     return false;
7635   // find border face between theFirstNode and theSecondNode
7636   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7637   if ( !curElem )
7638     return false;
7639
7640   theFaces.push_back( curElem );
7641   theNodes.push_back( theFirstNode );
7642   theNodes.push_back( theSecondNode );
7643
7644   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7645   //TIDSortedElemSet foundElems;
7646   bool needTheLast = ( theLastNode != 0 );
7647
7648   vector<const SMDS_MeshNode*> nodes;
7649   
7650   while ( nStart != theLastNode ) {
7651     if ( nStart == theFirstNode )
7652       return !needTheLast;
7653
7654     // find all free border faces sharing nStart
7655
7656     list< const SMDS_MeshElement* > curElemList;
7657     list< const SMDS_MeshNode* >    nStartList;
7658     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7659     while ( invElemIt->more() ) {
7660       const SMDS_MeshElement* e = invElemIt->next();
7661       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7662       {
7663         // get nodes
7664         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7665                       SMDS_MeshElement::iterator() );
7666         nodes.push_back( nodes[ 0 ]);
7667
7668         // check 2 links
7669         int iNode = 0, nbNodes = nodes.size() - 1;
7670         for ( iNode = 0; iNode < nbNodes; iNode++ )
7671           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7672                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7673               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7674           {
7675             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7676             curElemList.push_back( e );
7677           }
7678       }
7679     }
7680     // analyse the found
7681
7682     int nbNewBorders = curElemList.size();
7683     if ( nbNewBorders == 0 ) {
7684       // no free border furthermore
7685       return !needTheLast;
7686     }
7687     else if ( nbNewBorders == 1 ) {
7688       // one more element found
7689       nIgnore = nStart;
7690       nStart = nStartList.front();
7691       curElem = curElemList.front();
7692       theFaces.push_back( curElem );
7693       theNodes.push_back( nStart );
7694     }
7695     else {
7696       // several continuations found
7697       list< const SMDS_MeshElement* >::iterator curElemIt;
7698       list< const SMDS_MeshNode* >::iterator nStartIt;
7699       // check if one of them reached the last node
7700       if ( needTheLast ) {
7701         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7702              curElemIt!= curElemList.end();
7703              curElemIt++, nStartIt++ )
7704           if ( *nStartIt == theLastNode ) {
7705             theFaces.push_back( *curElemIt );
7706             theNodes.push_back( *nStartIt );
7707             return true;
7708           }
7709       }
7710       // find the best free border by the continuations
7711       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7712       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7713       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7714            curElemIt!= curElemList.end();
7715            curElemIt++, nStartIt++ )
7716       {
7717         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7718         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7719         // find one more free border
7720         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7721           cNL->clear();
7722           cFL->clear();
7723         }
7724         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7725           // choice: clear a worse one
7726           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7727           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7728           contNodes[ iWorse ].clear();
7729           contFaces[ iWorse ].clear();
7730         }
7731       }
7732       if ( contNodes[0].empty() && contNodes[1].empty() )
7733         return false;
7734
7735       // push_back the best free border
7736       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7737       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7738       //theNodes.pop_back(); // remove nIgnore
7739       theNodes.pop_back(); // remove nStart
7740       //theFaces.pop_back(); // remove curElem
7741       theNodes.splice( theNodes.end(), *cNL );
7742       theFaces.splice( theFaces.end(), *cFL );
7743       return true;
7744
7745     } // several continuations found
7746   } // while ( nStart != theLastNode )
7747
7748   return true;
7749 }
7750
7751 //=======================================================================
7752 //function : CheckFreeBorderNodes
7753 //purpose  : Return true if the tree nodes are on a free border
7754 //=======================================================================
7755
7756 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7757                                             const SMDS_MeshNode* theNode2,
7758                                             const SMDS_MeshNode* theNode3)
7759 {
7760   list< const SMDS_MeshNode* > nodes;
7761   list< const SMDS_MeshElement* > faces;
7762   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7763 }
7764
7765 //=======================================================================
7766 //function : SewFreeBorder
7767 //purpose  :
7768 //warning  : for border-to-side sewing theSideSecondNode is considered as
7769 //           the last side node and theSideThirdNode is not used
7770 //=======================================================================
7771
7772 SMESH_MeshEditor::Sew_Error
7773 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7774                                  const SMDS_MeshNode* theBordSecondNode,
7775                                  const SMDS_MeshNode* theBordLastNode,
7776                                  const SMDS_MeshNode* theSideFirstNode,
7777                                  const SMDS_MeshNode* theSideSecondNode,
7778                                  const SMDS_MeshNode* theSideThirdNode,
7779                                  const bool           theSideIsFreeBorder,
7780                                  const bool           toCreatePolygons,
7781                                  const bool           toCreatePolyedrs)
7782 {
7783   ClearLastCreated();
7784
7785   Sew_Error aResult = SEW_OK;
7786
7787   // ====================================
7788   //    find side nodes and elements
7789   // ====================================
7790
7791   list< const SMDS_MeshNode* >    nSide[ 2 ];
7792   list< const SMDS_MeshElement* > eSide[ 2 ];
7793   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7794   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7795
7796   // Free border 1
7797   // --------------
7798   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7799                       nSide[0], eSide[0])) {
7800     MESSAGE(" Free Border 1 not found " );
7801     aResult = SEW_BORDER1_NOT_FOUND;
7802   }
7803   if (theSideIsFreeBorder) {
7804     // Free border 2
7805     // --------------
7806     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7807                         nSide[1], eSide[1])) {
7808       MESSAGE(" Free Border 2 not found " );
7809       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7810     }
7811   }
7812   if ( aResult != SEW_OK )
7813     return aResult;
7814
7815   if (!theSideIsFreeBorder) {
7816     // Side 2
7817     // --------------
7818
7819     // -------------------------------------------------------------------------
7820     // Algo:
7821     // 1. If nodes to merge are not coincident, move nodes of the free border
7822     //    from the coord sys defined by the direction from the first to last
7823     //    nodes of the border to the correspondent sys of the side 2
7824     // 2. On the side 2, find the links most co-directed with the correspondent
7825     //    links of the free border
7826     // -------------------------------------------------------------------------
7827
7828     // 1. Since sewing may break if there are volumes to split on the side 2,
7829     //    we won't move nodes but just compute new coordinates for them
7830     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7831     TNodeXYZMap nBordXYZ;
7832     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7833     list< const SMDS_MeshNode* >::iterator nBordIt;
7834
7835     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7836     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7837     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7838     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7839     double tol2 = 1.e-8;
7840     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7841     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7842       // Need node movement.
7843
7844       // find X and Z axes to create trsf
7845       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7846       gp_Vec X = Zs ^ Zb;
7847       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7848         // Zb || Zs
7849         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7850
7851       // coord systems
7852       gp_Ax3 toBordAx( Pb1, Zb, X );
7853       gp_Ax3 fromSideAx( Ps1, Zs, X );
7854       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7855       // set trsf
7856       gp_Trsf toBordSys, fromSide2Sys;
7857       toBordSys.SetTransformation( toBordAx );
7858       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7859       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7860
7861       // move
7862       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7863         const SMDS_MeshNode* n = *nBordIt;
7864         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7865         toBordSys.Transforms( xyz );
7866         fromSide2Sys.Transforms( xyz );
7867         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7868       }
7869     }
7870     else {
7871       // just insert nodes XYZ in the nBordXYZ map
7872       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7873         const SMDS_MeshNode* n = *nBordIt;
7874         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7875       }
7876     }
7877
7878     // 2. On the side 2, find the links most co-directed with the correspondent
7879     //    links of the free border
7880
7881     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7882     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7883     sideNodes.push_back( theSideFirstNode );
7884
7885     bool hasVolumes = false;
7886     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7887     set<long> foundSideLinkIDs, checkedLinkIDs;
7888     SMDS_VolumeTool volume;
7889     //const SMDS_MeshNode* faceNodes[ 4 ];
7890
7891     const SMDS_MeshNode*    sideNode;
7892     const SMDS_MeshElement* sideElem  = 0;
7893     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7894     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7895     nBordIt = bordNodes.begin();
7896     nBordIt++;
7897     // border node position and border link direction to compare with
7898     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7899     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7900     // choose next side node by link direction or by closeness to
7901     // the current border node:
7902     bool searchByDir = ( *nBordIt != theBordLastNode );
7903     do {
7904       // find the next node on the Side 2
7905       sideNode = 0;
7906       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7907       long linkID;
7908       checkedLinkIDs.clear();
7909       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7910
7911       // loop on inverse elements of current node (prevSideNode) on the Side 2
7912       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7913       while ( invElemIt->more() )
7914       {
7915         const SMDS_MeshElement* elem = invElemIt->next();
7916         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7917         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7918         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7919         bool isVolume = volume.Set( elem );
7920         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7921         if ( isVolume ) // --volume
7922           hasVolumes = true;
7923         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7924           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7925           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7926           while ( nIt->more() ) {
7927             nodes[ iNode ] = cast2Node( nIt->next() );
7928             if ( nodes[ iNode++ ] == prevSideNode )
7929               iPrevNode = iNode - 1;
7930           }
7931           // there are 2 links to check
7932           nbNodes = 2;
7933         }
7934         else // --edge
7935           continue;
7936         // loop on links, to be precise, on the second node of links
7937         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7938           const SMDS_MeshNode* n = nodes[ iNode ];
7939           if ( isVolume ) {
7940             if ( !volume.IsLinked( n, prevSideNode ))
7941               continue;
7942           }
7943           else {
7944             if ( iNode ) // a node before prevSideNode
7945               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7946             else         // a node after prevSideNode
7947               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7948           }
7949           // check if this link was already used
7950           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7951           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7952           if (!isJustChecked &&
7953               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7954           {
7955             // test a link geometrically
7956             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7957             bool linkIsBetter = false;
7958             double dot = 0.0, dist = 0.0;
7959             if ( searchByDir ) { // choose most co-directed link
7960               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7961               linkIsBetter = ( dot > maxDot );
7962             }
7963             else { // choose link with the node closest to bordPos
7964               dist = ( nextXYZ - bordPos ).SquareModulus();
7965               linkIsBetter = ( dist < minDist );
7966             }
7967             if ( linkIsBetter ) {
7968               maxDot = dot;
7969               minDist = dist;
7970               linkID = iLink;
7971               sideNode = n;
7972               sideElem = elem;
7973             }
7974           }
7975         }
7976       } // loop on inverse elements of prevSideNode
7977
7978       if ( !sideNode ) {
7979         MESSAGE(" Can't find path by links of the Side 2 ");
7980         return SEW_BAD_SIDE_NODES;
7981       }
7982       sideNodes.push_back( sideNode );
7983       sideElems.push_back( sideElem );
7984       foundSideLinkIDs.insert ( linkID );
7985       prevSideNode = sideNode;
7986
7987       if ( *nBordIt == theBordLastNode )
7988         searchByDir = false;
7989       else {
7990         // find the next border link to compare with
7991         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7992         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7993         // move to next border node if sideNode is before forward border node (bordPos)
7994         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7995           prevBordNode = *nBordIt;
7996           nBordIt++;
7997           bordPos = nBordXYZ[ *nBordIt ];
7998           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7999           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8000         }
8001       }
8002     }
8003     while ( sideNode != theSideSecondNode );
8004
8005     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8006       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8007       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8008     }
8009   } // end nodes search on the side 2
8010
8011   // ============================
8012   // sew the border to the side 2
8013   // ============================
8014
8015   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8016   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8017
8018   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8019   if ( toMergeConformal && toCreatePolygons )
8020   {
8021     // do not merge quadrangles if polygons are OK (IPAL0052824)
8022     eIt[0] = eSide[0].begin();
8023     eIt[1] = eSide[1].begin();
8024     bool allQuads[2] = { true, true };
8025     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8026       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8027         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8028     }
8029     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8030   }
8031
8032   TListOfListOfNodes nodeGroupsToMerge;
8033   if (( toMergeConformal ) ||
8034       ( theSideIsFreeBorder && !theSideThirdNode )) {
8035
8036     // all nodes are to be merged
8037
8038     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8039          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8040          nIt[0]++, nIt[1]++ )
8041     {
8042       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8043       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8044       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8045     }
8046   }
8047   else {
8048
8049     // insert new nodes into the border and the side to get equal nb of segments
8050
8051     // get normalized parameters of nodes on the borders
8052     vector< double > param[ 2 ];
8053     param[0].resize( maxNbNodes );
8054     param[1].resize( maxNbNodes );
8055     int iNode, iBord;
8056     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8057       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8058       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8059       const SMDS_MeshNode* nPrev = *nIt;
8060       double bordLength = 0;
8061       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8062         const SMDS_MeshNode* nCur = *nIt;
8063         gp_XYZ segment (nCur->X() - nPrev->X(),
8064                         nCur->Y() - nPrev->Y(),
8065                         nCur->Z() - nPrev->Z());
8066         double segmentLen = segment.Modulus();
8067         bordLength += segmentLen;
8068         param[ iBord ][ iNode ] = bordLength;
8069         nPrev = nCur;
8070       }
8071       // normalize within [0,1]
8072       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8073         param[ iBord ][ iNode ] /= bordLength;
8074       }
8075     }
8076
8077     // loop on border segments
8078     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8079     int i[ 2 ] = { 0, 0 };
8080     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8081     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8082
8083     // element can be split while iterating on border if it has two edges in the border
8084     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8085     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8086
8087     TElemOfNodeListMap insertMap;
8088     TElemOfNodeListMap::iterator insertMapIt;
8089     // insertMap is
8090     // key:   elem to insert nodes into
8091     // value: 2 nodes to insert between + nodes to be inserted
8092     do {
8093       bool next[ 2 ] = { false, false };
8094
8095       // find min adjacent segment length after sewing
8096       double nextParam = 10., prevParam = 0;
8097       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8098         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8099           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8100         if ( i[ iBord ] > 0 )
8101           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8102       }
8103       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8104       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8105       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8106
8107       // choose to insert or to merge nodes
8108       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8109       if ( Abs( du ) <= minSegLen * 0.2 ) {
8110         // merge
8111         // ------
8112         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8113         const SMDS_MeshNode* n0 = *nIt[0];
8114         const SMDS_MeshNode* n1 = *nIt[1];
8115         nodeGroupsToMerge.back().push_back( n1 );
8116         nodeGroupsToMerge.back().push_back( n0 );
8117         // position of node of the border changes due to merge
8118         param[ 0 ][ i[0] ] += du;
8119         // move n1 for the sake of elem shape evaluation during insertion.
8120         // n1 will be removed by MergeNodes() anyway
8121         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8122         next[0] = next[1] = true;
8123       }
8124       else {
8125         // insert
8126         // ------
8127         int intoBord = ( du < 0 ) ? 0 : 1;
8128         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8129         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8130         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8131         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8132         if ( intoBord == 1 ) {
8133           // move node of the border to be on a link of elem of the side
8134           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8135           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8136           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8137           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8138         }
8139         elemReplaceMapIt = elemReplaceMap.find( elem );
8140         if ( elemReplaceMapIt != elemReplaceMap.end() )
8141           elem = elemReplaceMapIt->second;
8142
8143         insertMapIt = insertMap.find( elem );
8144         bool  notFound = ( insertMapIt == insertMap.end() );
8145         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8146         if ( otherLink ) {
8147           // insert into another link of the same element:
8148           // 1. perform insertion into the other link of the elem
8149           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8150           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8151           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8152           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8153           // 2. perform insertion into the link of adjacent faces
8154           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8155             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8156           }
8157           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8158             InsertNodesIntoLink( seg, n12, n22, nodeList );
8159           }
8160           if (toCreatePolyedrs) {
8161             // perform insertion into the links of adjacent volumes
8162             UpdateVolumes(n12, n22, nodeList);
8163           }
8164           // 3. find an element appeared on n1 and n2 after the insertion
8165           insertMap.erase( insertMapIt );
8166           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8167           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8168           elem = elem2;
8169         }
8170         if ( notFound || otherLink ) {
8171           // add element and nodes of the side into the insertMap
8172           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8173           (*insertMapIt).second.push_back( n1 );
8174           (*insertMapIt).second.push_back( n2 );
8175         }
8176         // add node to be inserted into elem
8177         (*insertMapIt).second.push_back( nIns );
8178         next[ 1 - intoBord ] = true;
8179       }
8180
8181       // go to the next segment
8182       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8183         if ( next[ iBord ] ) {
8184           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8185             eIt[ iBord ]++;
8186           nPrev[ iBord ] = *nIt[ iBord ];
8187           nIt[ iBord ]++; i[ iBord ]++;
8188         }
8189       }
8190     }
8191     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8192
8193     // perform insertion of nodes into elements
8194
8195     for (insertMapIt = insertMap.begin();
8196          insertMapIt != insertMap.end();
8197          insertMapIt++ )
8198     {
8199       const SMDS_MeshElement* elem = (*insertMapIt).first;
8200       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8201       if ( nodeList.size() < 3 ) continue;
8202       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8203       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8204
8205       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8206
8207       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8208         InsertNodesIntoLink( seg, n1, n2, nodeList );
8209       }
8210
8211       if ( !theSideIsFreeBorder ) {
8212         // look for and insert nodes into the faces adjacent to elem
8213         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8214           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8215         }
8216       }
8217       if (toCreatePolyedrs) {
8218         // perform insertion into the links of adjacent volumes
8219         UpdateVolumes(n1, n2, nodeList);
8220       }
8221     }
8222   } // end: insert new nodes
8223
8224   MergeNodes ( nodeGroupsToMerge );
8225
8226
8227   // Remove coincident segments
8228
8229   // get new segments
8230   TIDSortedElemSet segments;
8231   SMESH_SequenceOfElemPtr newFaces;
8232   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8233   {
8234     if ( !myLastCreatedElems[i] ) continue;
8235     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8236       segments.insert( segments.end(), myLastCreatedElems[i] );
8237     else
8238       newFaces.push_back( myLastCreatedElems[i] );
8239   }
8240   // get segments adjacent to merged nodes
8241   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8242   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8243   {
8244     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8245     if ( nodes.front()->IsNull() ) continue;
8246     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8247     while ( segIt->more() )
8248       segments.insert( segIt->next() );
8249   }
8250
8251   // find coincident
8252   TListOfListOfElementsID equalGroups;
8253   if ( !segments.empty() )
8254     FindEqualElements( segments, equalGroups );
8255   if ( !equalGroups.empty() )
8256   {
8257     // remove from segments those that will be removed
8258     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8259     for ( ; itGroups != equalGroups.end(); ++itGroups )
8260     {
8261       list< int >& group = *itGroups;
8262       list< int >::iterator id = group.begin();
8263       for ( ++id; id != group.end(); ++id )
8264         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8265           segments.erase( seg );
8266     }
8267     // remove equal segments
8268     MergeElements( equalGroups );
8269
8270     // restore myLastCreatedElems
8271     myLastCreatedElems = newFaces;
8272     TIDSortedElemSet::iterator seg = segments.begin();
8273     for ( ; seg != segments.end(); ++seg )
8274       myLastCreatedElems.push_back( *seg );
8275   }
8276
8277   return aResult;
8278 }
8279
8280 //=======================================================================
8281 //function : InsertNodesIntoLink
8282 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8283 //           and theBetweenNode2 and split theElement
8284 //=======================================================================
8285
8286 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8287                                            const SMDS_MeshNode*        theBetweenNode1,
8288                                            const SMDS_MeshNode*        theBetweenNode2,
8289                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8290                                            const bool                  toCreatePoly)
8291 {
8292   if ( !theElement ) return;
8293
8294   SMESHDS_Mesh *aMesh = GetMeshDS();
8295   vector<const SMDS_MeshElement*> newElems;
8296
8297   if ( theElement->GetType() == SMDSAbs_Edge )
8298   {
8299     theNodesToInsert.push_front( theBetweenNode1 );
8300     theNodesToInsert.push_back ( theBetweenNode2 );
8301     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8302     const SMDS_MeshNode* n1 = *n;
8303     for ( ++n; n != theNodesToInsert.end(); ++n )
8304     {
8305       const SMDS_MeshNode* n2 = *n;
8306       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8307         AddToSameGroups( seg, theElement, aMesh );
8308       else
8309         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8310       n1 = n2;
8311     }
8312     theNodesToInsert.pop_front();
8313     theNodesToInsert.pop_back();
8314
8315     if ( theElement->IsQuadratic() ) // add a not split part
8316     {
8317       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8318                                           theElement->end_nodes() );
8319       int iOther = 0, nbN = nodes.size();
8320       for ( ; iOther < nbN; ++iOther )
8321         if ( nodes[iOther] != theBetweenNode1 &&
8322              nodes[iOther] != theBetweenNode2 )
8323           break;
8324       if      ( iOther == 0 )
8325       {
8326         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8327           AddToSameGroups( seg, theElement, aMesh );
8328         else
8329           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8330       }
8331       else if ( iOther == 2 )
8332       {
8333         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8334           AddToSameGroups( seg, theElement, aMesh );
8335         else
8336           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8337       }
8338     }
8339     // treat new elements
8340     for ( size_t i = 0; i < newElems.size(); ++i )
8341       if ( newElems[i] )
8342       {
8343         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8344         myLastCreatedElems.push_back( newElems[i] );
8345       }
8346     ReplaceElemInGroups( theElement, newElems, aMesh );
8347     aMesh->RemoveElement( theElement );
8348     return;
8349
8350   } // if ( theElement->GetType() == SMDSAbs_Edge )
8351
8352   const SMDS_MeshElement* theFace = theElement;
8353   if ( theFace->GetType() != SMDSAbs_Face ) return;
8354
8355   // find indices of 2 link nodes and of the rest nodes
8356   int iNode = 0, il1, il2, i3, i4;
8357   il1 = il2 = i3 = i4 = -1;
8358   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8359
8360   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8361   while ( nodeIt->more() ) {
8362     const SMDS_MeshNode* n = nodeIt->next();
8363     if ( n == theBetweenNode1 )
8364       il1 = iNode;
8365     else if ( n == theBetweenNode2 )
8366       il2 = iNode;
8367     else if ( i3 < 0 )
8368       i3 = iNode;
8369     else
8370       i4 = iNode;
8371     nodes[ iNode++ ] = n;
8372   }
8373   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8374     return ;
8375
8376   // arrange link nodes to go one after another regarding the face orientation
8377   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8378   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8379   if ( reverse ) {
8380     iNode = il1;
8381     il1 = il2;
8382     il2 = iNode;
8383     aNodesToInsert.reverse();
8384   }
8385   // check that not link nodes of a quadrangles are in good order
8386   int nbFaceNodes = theFace->NbNodes();
8387   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8388     iNode = i3;
8389     i3 = i4;
8390     i4 = iNode;
8391   }
8392
8393   if (toCreatePoly || theFace->IsPoly()) {
8394
8395     iNode = 0;
8396     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8397
8398     // add nodes of face up to first node of link
8399     bool isFLN = false;
8400     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8401     while ( nodeIt->more() && !isFLN ) {
8402       const SMDS_MeshNode* n = nodeIt->next();
8403       poly_nodes[iNode++] = n;
8404       isFLN = ( n == nodes[il1] );
8405     }
8406     // add nodes to insert
8407     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8408     for (; nIt != aNodesToInsert.end(); nIt++) {
8409       poly_nodes[iNode++] = *nIt;
8410     }
8411     // add nodes of face starting from last node of link
8412     while ( nodeIt->more() ) {
8413       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8414       poly_nodes[iNode++] = n;
8415     }
8416
8417     // make a new face
8418     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8419   }
8420
8421   else if ( !theFace->IsQuadratic() )
8422   {
8423     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8424     int nbLinkNodes = 2 + aNodesToInsert.size();
8425     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8426     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8427     linkNodes[ 0 ] = nodes[ il1 ];
8428     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8429     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8430     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8431       linkNodes[ iNode++ ] = *nIt;
8432     }
8433     // decide how to split a quadrangle: compare possible variants
8434     // and choose which of splits to be a quadrangle
8435     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8436     if ( nbFaceNodes == 3 ) {
8437       iBestQuad = nbSplits;
8438       i4 = i3;
8439     }
8440     else if ( nbFaceNodes == 4 ) {
8441       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8442       double aBestRate = DBL_MAX;
8443       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8444         i1 = 0; i2 = 1;
8445         double aBadRate = 0;
8446         // evaluate elements quality
8447         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8448           if ( iSplit == iQuad ) {
8449             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8450                                    linkNodes[ i2++ ],
8451                                    nodes[ i3 ],
8452                                    nodes[ i4 ]);
8453             aBadRate += getBadRate( &quad, aCrit );
8454           }
8455           else {
8456             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8457                                    linkNodes[ i2++ ],
8458                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8459             aBadRate += getBadRate( &tria, aCrit );
8460           }
8461         }
8462         // choice
8463         if ( aBadRate < aBestRate ) {
8464           iBestQuad = iQuad;
8465           aBestRate = aBadRate;
8466         }
8467       }
8468     }
8469
8470     // create new elements
8471     i1 = 0; i2 = 1;
8472     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8473     {
8474       if ( iSplit == iBestQuad )
8475         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8476                                             linkNodes[ i2++ ],
8477                                             nodes[ i3 ],
8478                                             nodes[ i4 ]));
8479       else
8480         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8481                                             linkNodes[ i2++ ],
8482                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8483     }
8484
8485     const SMDS_MeshNode* newNodes[ 4 ];
8486     newNodes[ 0 ] = linkNodes[ i1 ];
8487     newNodes[ 1 ] = linkNodes[ i2 ];
8488     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8489     newNodes[ 3 ] = nodes[ i4 ];
8490     if (iSplit == iBestQuad)
8491       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8492     else
8493       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8494
8495   } // end if(!theFace->IsQuadratic())
8496
8497   else { // theFace is quadratic
8498     // we have to split theFace on simple triangles and one simple quadrangle
8499     int tmp = il1/2;
8500     int nbshift = tmp*2;
8501     // shift nodes in nodes[] by nbshift
8502     int i,j;
8503     for(i=0; i<nbshift; i++) {
8504       const SMDS_MeshNode* n = nodes[0];
8505       for(j=0; j<nbFaceNodes-1; j++) {
8506         nodes[j] = nodes[j+1];
8507       }
8508       nodes[nbFaceNodes-1] = n;
8509     }
8510     il1 = il1 - nbshift;
8511     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8512     //   n0      n1     n2    n0      n1     n2
8513     //     +-----+-----+        +-----+-----+
8514     //      \         /         |           |
8515     //       \       /          |           |
8516     //      n5+     +n3       n7+           +n3
8517     //         \   /            |           |
8518     //          \ /             |           |
8519     //           +              +-----+-----+
8520     //           n4           n6      n5     n4
8521
8522     // create new elements
8523     int n1,n2,n3;
8524     if ( nbFaceNodes == 6 ) { // quadratic triangle
8525       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8526       if ( theFace->IsMediumNode(nodes[il1]) ) {
8527         // create quadrangle
8528         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8529         n1 = 1;
8530         n2 = 2;
8531         n3 = 3;
8532       }
8533       else {
8534         // create quadrangle
8535         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8536         n1 = 0;
8537         n2 = 1;
8538         n3 = 5;
8539       }
8540     }
8541     else { // nbFaceNodes==8 - quadratic quadrangle
8542       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8543       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8544       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8545       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8546         // create quadrangle
8547         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8548         n1 = 1;
8549         n2 = 2;
8550         n3 = 3;
8551       }
8552       else {
8553         // create quadrangle
8554         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8555         n1 = 0;
8556         n2 = 1;
8557         n3 = 7;
8558       }
8559     }
8560     // create needed triangles using n1,n2,n3 and inserted nodes
8561     int nbn = 2 + aNodesToInsert.size();
8562     vector<const SMDS_MeshNode*> aNodes(nbn);
8563     aNodes[0    ] = nodes[n1];
8564     aNodes[nbn-1] = nodes[n2];
8565     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8566     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8567       aNodes[iNode++] = *nIt;
8568     }
8569     for ( i = 1; i < nbn; i++ )
8570       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8571   }
8572
8573   // remove the old face
8574   for ( size_t i = 0; i < newElems.size(); ++i )
8575     if ( newElems[i] )
8576     {
8577       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8578       myLastCreatedElems.push_back( newElems[i] );
8579     }
8580   ReplaceElemInGroups( theFace, newElems, aMesh );
8581   aMesh->RemoveElement(theFace);
8582
8583 } // InsertNodesIntoLink()
8584
8585 //=======================================================================
8586 //function : UpdateVolumes
8587 //purpose  :
8588 //=======================================================================
8589
8590 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8591                                       const SMDS_MeshNode*        theBetweenNode2,
8592                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8593 {
8594   ClearLastCreated();
8595
8596   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8597   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8598     const SMDS_MeshElement* elem = invElemIt->next();
8599
8600     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8601     SMDS_VolumeTool aVolume (elem);
8602     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8603       continue;
8604
8605     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8606     int iface, nbFaces = aVolume.NbFaces();
8607     vector<const SMDS_MeshNode *> poly_nodes;
8608     vector<int> quantities (nbFaces);
8609
8610     for (iface = 0; iface < nbFaces; iface++) {
8611       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8612       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8613       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8614
8615       for (int inode = 0; inode < nbFaceNodes; inode++) {
8616         poly_nodes.push_back(faceNodes[inode]);
8617
8618         if (nbInserted == 0) {
8619           if (faceNodes[inode] == theBetweenNode1) {
8620             if (faceNodes[inode + 1] == theBetweenNode2) {
8621               nbInserted = theNodesToInsert.size();
8622
8623               // add nodes to insert
8624               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8625               for (; nIt != theNodesToInsert.end(); nIt++) {
8626                 poly_nodes.push_back(*nIt);
8627               }
8628             }
8629           }
8630           else if (faceNodes[inode] == theBetweenNode2) {
8631             if (faceNodes[inode + 1] == theBetweenNode1) {
8632               nbInserted = theNodesToInsert.size();
8633
8634               // add nodes to insert in reversed order
8635               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8636               nIt--;
8637               for (; nIt != theNodesToInsert.begin(); nIt--) {
8638                 poly_nodes.push_back(*nIt);
8639               }
8640               poly_nodes.push_back(*nIt);
8641             }
8642           }
8643           else {
8644           }
8645         }
8646       }
8647       quantities[iface] = nbFaceNodes + nbInserted;
8648     }
8649
8650     // Replace the volume
8651     SMESHDS_Mesh *aMesh = GetMeshDS();
8652
8653     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8654     {
8655       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8656       myLastCreatedElems.push_back( newElem );
8657       ReplaceElemInGroups( elem, newElem, aMesh );
8658     }
8659     aMesh->RemoveElement( elem );
8660   }
8661 }
8662
8663 namespace
8664 {
8665   //================================================================================
8666   /*!
8667    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8668    */
8669   //================================================================================
8670
8671   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8672                            vector<const SMDS_MeshNode *> & nodes,
8673                            vector<int> &                   nbNodeInFaces )
8674   {
8675     nodes.clear();
8676     nbNodeInFaces.clear();
8677     SMDS_VolumeTool vTool ( elem );
8678     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8679     {
8680       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8681       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8682       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8683     }
8684   }
8685 }
8686
8687 //=======================================================================
8688 /*!
8689  * \brief Convert elements contained in a sub-mesh to quadratic
8690  * \return int - nb of checked elements
8691  */
8692 //=======================================================================
8693
8694 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8695                                              SMESH_MesherHelper& theHelper,
8696                                              const bool          theForce3d)
8697 {
8698   //MESSAGE("convertElemToQuadratic");
8699   int nbElem = 0;
8700   if( !theSm ) return nbElem;
8701
8702   vector<int> nbNodeInFaces;
8703   vector<const SMDS_MeshNode *> nodes;
8704   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8705   while(ElemItr->more())
8706   {
8707     nbElem++;
8708     const SMDS_MeshElement* elem = ElemItr->next();
8709     if( !elem ) continue;
8710
8711     // analyse a necessity of conversion
8712     const SMDSAbs_ElementType aType = elem->GetType();
8713     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8714       continue;
8715     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8716     bool hasCentralNodes = false;
8717     if ( elem->IsQuadratic() )
8718     {
8719       bool alreadyOK;
8720       switch ( aGeomType ) {
8721       case SMDSEntity_Quad_Triangle:
8722       case SMDSEntity_Quad_Quadrangle:
8723       case SMDSEntity_Quad_Hexa:
8724       case SMDSEntity_Quad_Penta:
8725         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8726
8727       case SMDSEntity_BiQuad_Triangle:
8728       case SMDSEntity_BiQuad_Quadrangle:
8729       case SMDSEntity_TriQuad_Hexa:
8730       case SMDSEntity_BiQuad_Penta:
8731         alreadyOK = theHelper.GetIsBiQuadratic();
8732         hasCentralNodes = true;
8733         break;
8734       default:
8735         alreadyOK = true;
8736       }
8737       // take into account already present medium nodes
8738       switch ( aType ) {
8739       case SMDSAbs_Volume:
8740         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8741       case SMDSAbs_Face:
8742         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8743       case SMDSAbs_Edge:
8744         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8745       default:;
8746       }
8747       if ( alreadyOK )
8748         continue;
8749     }
8750     // get elem data needed to re-create it
8751     //
8752     const int id      = elem->GetID();
8753     const int nbNodes = elem->NbCornerNodes();
8754     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8755     if ( aGeomType == SMDSEntity_Polyhedra )
8756       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8757     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8758       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8759
8760     // remove a linear element
8761     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8762
8763     // remove central nodes of biquadratic elements (biquad->quad conversion)
8764     if ( hasCentralNodes )
8765       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8766         if ( nodes[i]->NbInverseElements() == 0 )
8767           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8768
8769     const SMDS_MeshElement* NewElem = 0;
8770
8771     switch( aType )
8772     {
8773     case SMDSAbs_Edge :
8774     {
8775       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8776       break;
8777     }
8778     case SMDSAbs_Face :
8779     {
8780       switch(nbNodes)
8781       {
8782       case 3:
8783         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8784         break;
8785       case 4:
8786         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8787         break;
8788       default:
8789         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8790       }
8791       break;
8792     }
8793     case SMDSAbs_Volume :
8794     {
8795       switch( aGeomType )
8796       {
8797       case SMDSEntity_Tetra:
8798         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8799         break;
8800       case SMDSEntity_Pyramid:
8801         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8802         break;
8803       case SMDSEntity_Penta:
8804       case SMDSEntity_Quad_Penta:
8805       case SMDSEntity_BiQuad_Penta:
8806         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8807         break;
8808       case SMDSEntity_Hexa:
8809       case SMDSEntity_Quad_Hexa:
8810       case SMDSEntity_TriQuad_Hexa:
8811         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8812                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8813         break;
8814       case SMDSEntity_Hexagonal_Prism:
8815       default:
8816         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8817       }
8818       break;
8819     }
8820     default :
8821       continue;
8822     }
8823     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8824     if( NewElem && NewElem->getshapeId() < 1 )
8825       theSm->AddElement( NewElem );
8826   }
8827   return nbElem;
8828 }
8829 //=======================================================================
8830 //function : ConvertToQuadratic
8831 //purpose  :
8832 //=======================================================================
8833
8834 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8835 {
8836   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8837   SMESHDS_Mesh* meshDS = GetMeshDS();
8838
8839   SMESH_MesherHelper aHelper(*myMesh);
8840
8841   aHelper.SetIsQuadratic( true );
8842   aHelper.SetIsBiQuadratic( theToBiQuad );
8843   aHelper.SetElementsOnShape(true);
8844   aHelper.ToFixNodeParameters( true );
8845
8846   // convert elements assigned to sub-meshes
8847   int nbCheckedElems = 0;
8848   if ( myMesh->HasShapeToMesh() )
8849   {
8850     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8851     {
8852       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8853       while ( smIt->more() ) {
8854         SMESH_subMesh* sm = smIt->next();
8855         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8856           aHelper.SetSubShape( sm->GetSubShape() );
8857           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8858         }
8859       }
8860     }
8861   }
8862
8863   // convert elements NOT assigned to sub-meshes
8864   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8865   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8866   {
8867     aHelper.SetElementsOnShape(false);
8868     SMESHDS_SubMesh *smDS = 0;
8869
8870     // convert edges
8871     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8872     while( aEdgeItr->more() )
8873     {
8874       const SMDS_MeshEdge* edge = aEdgeItr->next();
8875       if ( !edge->IsQuadratic() )
8876       {
8877         int                  id = edge->GetID();
8878         const SMDS_MeshNode* n1 = edge->GetNode(0);
8879         const SMDS_MeshNode* n2 = edge->GetNode(1);
8880
8881         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8882
8883         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8884         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8885       }
8886       else
8887       {
8888         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8889       }
8890     }
8891
8892     // convert faces
8893     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8894     while( aFaceItr->more() )
8895     {
8896       const SMDS_MeshFace* face = aFaceItr->next();
8897       if ( !face ) continue;
8898       
8899       const SMDSAbs_EntityType type = face->GetEntityType();
8900       bool alreadyOK;
8901       switch( type )
8902       {
8903       case SMDSEntity_Quad_Triangle:
8904       case SMDSEntity_Quad_Quadrangle:
8905         alreadyOK = !theToBiQuad;
8906         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8907         break;
8908       case SMDSEntity_BiQuad_Triangle:
8909       case SMDSEntity_BiQuad_Quadrangle:
8910         alreadyOK = theToBiQuad;
8911         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8912         break;
8913       default: alreadyOK = false;
8914       }
8915       if ( alreadyOK )
8916         continue;
8917
8918       const int id = face->GetID();
8919       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8920
8921       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8922
8923       SMDS_MeshFace * NewFace = 0;
8924       switch( type )
8925       {
8926       case SMDSEntity_Triangle:
8927       case SMDSEntity_Quad_Triangle:
8928       case SMDSEntity_BiQuad_Triangle:
8929         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8930         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8931           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8932         break;
8933
8934       case SMDSEntity_Quadrangle:
8935       case SMDSEntity_Quad_Quadrangle:
8936       case SMDSEntity_BiQuad_Quadrangle:
8937         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8938         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8939           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8940         break;
8941
8942       default:;
8943         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8944       }
8945       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8946     }
8947
8948     // convert volumes
8949     vector<int> nbNodeInFaces;
8950     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8951     while(aVolumeItr->more())
8952     {
8953       const SMDS_MeshVolume* volume = aVolumeItr->next();
8954       if ( !volume ) continue;
8955
8956       const SMDSAbs_EntityType type = volume->GetEntityType();
8957       if ( volume->IsQuadratic() )
8958       {
8959         bool alreadyOK;
8960         switch ( type )
8961         {
8962         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8963         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8964         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8965         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8966         default:                      alreadyOK = true;
8967         }
8968         if ( alreadyOK )
8969         {
8970           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8971           continue;
8972         }
8973       }
8974       const int id = volume->GetID();
8975       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8976       if ( type == SMDSEntity_Polyhedra )
8977         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8978       else if ( type == SMDSEntity_Hexagonal_Prism )
8979         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8980
8981       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8982
8983       SMDS_MeshVolume * NewVolume = 0;
8984       switch ( type )
8985       {
8986       case SMDSEntity_Tetra:
8987         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8988         break;
8989       case SMDSEntity_Hexa:
8990       case SMDSEntity_Quad_Hexa:
8991       case SMDSEntity_TriQuad_Hexa:
8992         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8993                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8994         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8995           if ( nodes[i]->NbInverseElements() == 0 )
8996             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8997         break;
8998       case SMDSEntity_Pyramid:
8999         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9000                                       nodes[3], nodes[4], id, theForce3d);
9001         break;
9002       case SMDSEntity_Penta:
9003       case SMDSEntity_Quad_Penta:
9004       case SMDSEntity_BiQuad_Penta:
9005         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9006                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9007         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9008           if ( nodes[i]->NbInverseElements() == 0 )
9009             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9010         break;
9011       case SMDSEntity_Hexagonal_Prism:
9012       default:
9013         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9014       }
9015       ReplaceElemInGroups(volume, NewVolume, meshDS);
9016     }
9017   }
9018
9019   if ( !theForce3d )
9020   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9021     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9022     // aHelper.FixQuadraticElements(myError);
9023     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9024   }
9025 }
9026
9027 //================================================================================
9028 /*!
9029  * \brief Makes given elements quadratic
9030  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9031  *  \param theElements - elements to make quadratic
9032  */
9033 //================================================================================
9034
9035 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9036                                           TIDSortedElemSet& theElements,
9037                                           const bool        theToBiQuad)
9038 {
9039   if ( theElements.empty() ) return;
9040
9041   // we believe that all theElements are of the same type
9042   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9043
9044   // get all nodes shared by theElements
9045   TIDSortedNodeSet allNodes;
9046   TIDSortedElemSet::iterator eIt = theElements.begin();
9047   for ( ; eIt != theElements.end(); ++eIt )
9048     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9049
9050   // complete theElements with elements of lower dim whose all nodes are in allNodes
9051
9052   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9053   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9054   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9055   for ( ; nIt != allNodes.end(); ++nIt )
9056   {
9057     const SMDS_MeshNode* n = *nIt;
9058     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9059     while ( invIt->more() )
9060     {
9061       const SMDS_MeshElement*      e = invIt->next();
9062       const SMDSAbs_ElementType type = e->GetType();
9063       if ( e->IsQuadratic() )
9064       {
9065         quadAdjacentElems[ type ].insert( e );
9066
9067         bool alreadyOK;
9068         switch ( e->GetEntityType() ) {
9069         case SMDSEntity_Quad_Triangle:
9070         case SMDSEntity_Quad_Quadrangle:
9071         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9072         case SMDSEntity_BiQuad_Triangle:
9073         case SMDSEntity_BiQuad_Quadrangle:
9074         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9075         default:                           alreadyOK = true;
9076         }
9077         if ( alreadyOK )
9078           continue;
9079       }
9080       if ( type >= elemType )
9081         continue; // same type or more complex linear element
9082
9083       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9084         continue; // e is already checked
9085
9086       // check nodes
9087       bool allIn = true;
9088       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9089       while ( nodeIt->more() && allIn )
9090         allIn = allNodes.count( nodeIt->next() );
9091       if ( allIn )
9092         theElements.insert(e );
9093     }
9094   }
9095
9096   SMESH_MesherHelper helper(*myMesh);
9097   helper.SetIsQuadratic( true );
9098   helper.SetIsBiQuadratic( theToBiQuad );
9099
9100   // add links of quadratic adjacent elements to the helper
9101
9102   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9103     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9104           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9105     {
9106       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9107     }
9108   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9109     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9110           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9111     {
9112       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9113     }
9114   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9115     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9116           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9117     {
9118       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9119     }
9120
9121   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9122
9123   SMESHDS_Mesh*  meshDS = GetMeshDS();
9124   SMESHDS_SubMesh* smDS = 0;
9125   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9126   {
9127     const SMDS_MeshElement* elem = *eIt;
9128
9129     bool alreadyOK;
9130     int nbCentralNodes = 0;
9131     switch ( elem->GetEntityType() ) {
9132       // linear convertible
9133     case SMDSEntity_Edge:
9134     case SMDSEntity_Triangle:
9135     case SMDSEntity_Quadrangle:
9136     case SMDSEntity_Tetra:
9137     case SMDSEntity_Pyramid:
9138     case SMDSEntity_Hexa:
9139     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9140       // quadratic that can become bi-quadratic
9141     case SMDSEntity_Quad_Triangle:
9142     case SMDSEntity_Quad_Quadrangle:
9143     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9144       // bi-quadratic
9145     case SMDSEntity_BiQuad_Triangle:
9146     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9147     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9148       // the rest
9149     default:                           alreadyOK = true;
9150     }
9151     if ( alreadyOK ) continue;
9152
9153     const SMDSAbs_ElementType type = elem->GetType();
9154     const int                   id = elem->GetID();
9155     const int              nbNodes = elem->NbCornerNodes();
9156     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9157
9158     helper.SetSubShape( elem->getshapeId() );
9159
9160     if ( !smDS || !smDS->Contains( elem ))
9161       smDS = meshDS->MeshElements( elem->getshapeId() );
9162     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9163
9164     SMDS_MeshElement * newElem = 0;
9165     switch( nbNodes )
9166     {
9167     case 4: // cases for most frequently used element types go first (for optimization)
9168       if ( type == SMDSAbs_Volume )
9169         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9170       else
9171         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9172       break;
9173     case 8:
9174       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9175                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9176       break;
9177     case 3:
9178       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9179       break;
9180     case 2:
9181       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9182       break;
9183     case 5:
9184       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9185                                  nodes[4], id, theForce3d);
9186       break;
9187     case 6:
9188       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9189                                  nodes[4], nodes[5], id, theForce3d);
9190       break;
9191     default:;
9192     }
9193     ReplaceElemInGroups( elem, newElem, meshDS);
9194     if( newElem && smDS )
9195       smDS->AddElement( newElem );
9196
9197     // remove central nodes
9198     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9199       if ( nodes[i]->NbInverseElements() == 0 )
9200         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9201
9202   } // loop on theElements
9203
9204   if ( !theForce3d )
9205   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9206     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9207     // helper.FixQuadraticElements( myError );
9208     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9209   }
9210 }
9211
9212 //=======================================================================
9213 /*!
9214  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9215  * \return int - nb of checked elements
9216  */
9217 //=======================================================================
9218
9219 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9220                                      SMDS_ElemIteratorPtr theItr,
9221                                      const int            /*theShapeID*/)
9222 {
9223   int nbElem = 0;
9224   SMESHDS_Mesh* meshDS = GetMeshDS();
9225   ElemFeatures elemType;
9226   vector<const SMDS_MeshNode *> nodes;
9227
9228   while( theItr->more() )
9229   {
9230     const SMDS_MeshElement* elem = theItr->next();
9231     nbElem++;
9232     if( elem && elem->IsQuadratic())
9233     {
9234       // get elem data
9235       int nbCornerNodes = elem->NbCornerNodes();
9236       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9237
9238       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9239
9240       //remove a quadratic element
9241       if ( !theSm || !theSm->Contains( elem ))
9242         theSm = meshDS->MeshElements( elem->getshapeId() );
9243       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9244
9245       // remove medium nodes
9246       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9247         if ( nodes[i]->NbInverseElements() == 0 )
9248           meshDS->RemoveFreeNode( nodes[i], theSm );
9249
9250       // add a linear element
9251       nodes.resize( nbCornerNodes );
9252       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9253       ReplaceElemInGroups(elem, newElem, meshDS);
9254       if( theSm && newElem )
9255         theSm->AddElement( newElem );
9256     }
9257   }
9258   return nbElem;
9259 }
9260
9261 //=======================================================================
9262 //function : ConvertFromQuadratic
9263 //purpose  :
9264 //=======================================================================
9265
9266 bool SMESH_MeshEditor::ConvertFromQuadratic()
9267 {
9268   int nbCheckedElems = 0;
9269   if ( myMesh->HasShapeToMesh() )
9270   {
9271     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9272     {
9273       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9274       while ( smIt->more() ) {
9275         SMESH_subMesh* sm = smIt->next();
9276         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9277           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9278       }
9279     }
9280   }
9281
9282   int totalNbElems =
9283     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9284   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9285   {
9286     SMESHDS_SubMesh *aSM = 0;
9287     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9288   }
9289
9290   return true;
9291 }
9292
9293 namespace
9294 {
9295   //================================================================================
9296   /*!
9297    * \brief Return true if all medium nodes of the element are in the node set
9298    */
9299   //================================================================================
9300
9301   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9302   {
9303     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9304       if ( !nodeSet.count( elem->GetNode(i) ))
9305         return false;
9306     return true;
9307   }
9308 }
9309
9310 //================================================================================
9311 /*!
9312  * \brief Makes given elements linear
9313  */
9314 //================================================================================
9315
9316 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9317 {
9318   if ( theElements.empty() ) return;
9319
9320   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9321   set<int> mediumNodeIDs;
9322   TIDSortedElemSet::iterator eIt = theElements.begin();
9323   for ( ; eIt != theElements.end(); ++eIt )
9324   {
9325     const SMDS_MeshElement* e = *eIt;
9326     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9327       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9328   }
9329
9330   // replace given elements by linear ones
9331   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9332   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9333
9334   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9335   // except those elements sharing medium nodes of quadratic element whose medium nodes
9336   // are not all in mediumNodeIDs
9337
9338   // get remaining medium nodes
9339   TIDSortedNodeSet mediumNodes;
9340   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9341   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9342     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9343       mediumNodes.insert( mediumNodes.end(), n );
9344
9345   // find more quadratic elements to convert
9346   TIDSortedElemSet moreElemsToConvert;
9347   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9348   for ( ; nIt != mediumNodes.end(); ++nIt )
9349   {
9350     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9351     while ( invIt->more() )
9352     {
9353       const SMDS_MeshElement* e = invIt->next();
9354       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9355       {
9356         // find a more complex element including e and
9357         // whose medium nodes are not in mediumNodes
9358         bool complexFound = false;
9359         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9360         {
9361           SMDS_ElemIteratorPtr invIt2 =
9362             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9363           while ( invIt2->more() )
9364           {
9365             const SMDS_MeshElement* eComplex = invIt2->next();
9366             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9367             {
9368               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9369               if ( nbCommonNodes == e->NbNodes())
9370               {
9371                 complexFound = true;
9372                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9373                 break;
9374               }
9375             }
9376           }
9377         }
9378         if ( !complexFound )
9379           moreElemsToConvert.insert( e );
9380       }
9381     }
9382   }
9383   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9384   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9385 }
9386
9387 //=======================================================================
9388 //function : SewSideElements
9389 //purpose  :
9390 //=======================================================================
9391
9392 SMESH_MeshEditor::Sew_Error
9393 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9394                                    TIDSortedElemSet&    theSide2,
9395                                    const SMDS_MeshNode* theFirstNode1,
9396                                    const SMDS_MeshNode* theFirstNode2,
9397                                    const SMDS_MeshNode* theSecondNode1,
9398                                    const SMDS_MeshNode* theSecondNode2)
9399 {
9400   ClearLastCreated();
9401
9402   if ( theSide1.size() != theSide2.size() )
9403     return SEW_DIFF_NB_OF_ELEMENTS;
9404
9405   Sew_Error aResult = SEW_OK;
9406   // Algo:
9407   // 1. Build set of faces representing each side
9408   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9409   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9410
9411   // =======================================================================
9412   // 1. Build set of faces representing each side:
9413   // =======================================================================
9414   // a. build set of nodes belonging to faces
9415   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9416   // c. create temporary faces representing side of volumes if correspondent
9417   //    face does not exist
9418
9419   SMESHDS_Mesh* aMesh = GetMeshDS();
9420   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9421   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9422   TIDSortedElemSet             faceSet1, faceSet2;
9423   set<const SMDS_MeshElement*> volSet1,  volSet2;
9424   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9425   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9426   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9427   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9428   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9429   int iSide, iFace, iNode;
9430
9431   list<const SMDS_MeshElement* > tempFaceList;
9432   for ( iSide = 0; iSide < 2; iSide++ ) {
9433     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9434     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9435     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9436     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9437     set<const SMDS_MeshElement*>::iterator vIt;
9438     TIDSortedElemSet::iterator eIt;
9439     set<const SMDS_MeshNode*>::iterator    nIt;
9440
9441     // check that given nodes belong to given elements
9442     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9443     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9444     int firstIndex = -1, secondIndex = -1;
9445     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9446       const SMDS_MeshElement* elem = *eIt;
9447       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9448       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9449       if ( firstIndex > -1 && secondIndex > -1 ) break;
9450     }
9451     if ( firstIndex < 0 || secondIndex < 0 ) {
9452       // we can simply return until temporary faces created
9453       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9454     }
9455
9456     // -----------------------------------------------------------
9457     // 1a. Collect nodes of existing faces
9458     //     and build set of face nodes in order to detect missing
9459     //     faces corresponding to sides of volumes
9460     // -----------------------------------------------------------
9461
9462     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9463
9464     // loop on the given element of a side
9465     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9466       //const SMDS_MeshElement* elem = *eIt;
9467       const SMDS_MeshElement* elem = *eIt;
9468       if ( elem->GetType() == SMDSAbs_Face ) {
9469         faceSet->insert( elem );
9470         set <const SMDS_MeshNode*> faceNodeSet;
9471         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9472         while ( nodeIt->more() ) {
9473           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9474           nodeSet->insert( n );
9475           faceNodeSet.insert( n );
9476         }
9477         setOfFaceNodeSet.insert( faceNodeSet );
9478       }
9479       else if ( elem->GetType() == SMDSAbs_Volume )
9480         volSet->insert( elem );
9481     }
9482     // ------------------------------------------------------------------------------
9483     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9484     // ------------------------------------------------------------------------------
9485
9486     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9487       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9488       while ( fIt->more() ) { // loop on faces sharing a node
9489         const SMDS_MeshElement* f = fIt->next();
9490         if ( faceSet->find( f ) == faceSet->end() ) {
9491           // check if all nodes are in nodeSet and
9492           // complete setOfFaceNodeSet if they are
9493           set <const SMDS_MeshNode*> faceNodeSet;
9494           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9495           bool allInSet = true;
9496           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9497             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9498             if ( nodeSet->find( n ) == nodeSet->end() )
9499               allInSet = false;
9500             else
9501               faceNodeSet.insert( n );
9502           }
9503           if ( allInSet ) {
9504             faceSet->insert( f );
9505             setOfFaceNodeSet.insert( faceNodeSet );
9506           }
9507         }
9508       }
9509     }
9510
9511     // -------------------------------------------------------------------------
9512     // 1c. Create temporary faces representing sides of volumes if correspondent
9513     //     face does not exist
9514     // -------------------------------------------------------------------------
9515
9516     if ( !volSet->empty() ) {
9517       //int nodeSetSize = nodeSet->size();
9518
9519       // loop on given volumes
9520       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9521         SMDS_VolumeTool vol (*vIt);
9522         // loop on volume faces: find free faces
9523         // --------------------------------------
9524         list<const SMDS_MeshElement* > freeFaceList;
9525         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9526           if ( !vol.IsFreeFace( iFace ))
9527             continue;
9528           // check if there is already a face with same nodes in a face set
9529           const SMDS_MeshElement* aFreeFace = 0;
9530           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9531           int nbNodes = vol.NbFaceNodes( iFace );
9532           set <const SMDS_MeshNode*> faceNodeSet;
9533           vol.GetFaceNodes( iFace, faceNodeSet );
9534           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9535           if ( isNewFace ) {
9536             // no such a face is given but it still can exist, check it
9537             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9538             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9539           }
9540           if ( !aFreeFace ) {
9541             // create a temporary face
9542             if ( nbNodes == 3 ) {
9543               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9544               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9545             }
9546             else if ( nbNodes == 4 ) {
9547               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9548               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9549             }
9550             else {
9551               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9552               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9553               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9554             }
9555             if ( aFreeFace )
9556               tempFaceList.push_back( aFreeFace );
9557           }
9558
9559           if ( aFreeFace )
9560             freeFaceList.push_back( aFreeFace );
9561
9562         } // loop on faces of a volume
9563
9564         // choose one of several free faces of a volume
9565         // --------------------------------------------
9566         if ( freeFaceList.size() > 1 ) {
9567           // choose a face having max nb of nodes shared by other elems of a side
9568           int maxNbNodes = -1;
9569           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9570           while ( fIt != freeFaceList.end() ) { // loop on free faces
9571             int nbSharedNodes = 0;
9572             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9573             while ( nodeIt->more() ) { // loop on free face nodes
9574               const SMDS_MeshNode* n =
9575                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9576               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9577               while ( invElemIt->more() ) {
9578                 const SMDS_MeshElement* e = invElemIt->next();
9579                 nbSharedNodes += faceSet->count( e );
9580                 nbSharedNodes += elemSet->count( e );
9581               }
9582             }
9583             if ( nbSharedNodes > maxNbNodes ) {
9584               maxNbNodes = nbSharedNodes;
9585               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9586             }
9587             else if ( nbSharedNodes == maxNbNodes ) {
9588               fIt++;
9589             }
9590             else {
9591               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9592             }
9593           }
9594           if ( freeFaceList.size() > 1 )
9595           {
9596             // could not choose one face, use another way
9597             // choose a face most close to the bary center of the opposite side
9598             gp_XYZ aBC( 0., 0., 0. );
9599             set <const SMDS_MeshNode*> addedNodes;
9600             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9601             eIt = elemSet2->begin();
9602             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9603               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9604               while ( nodeIt->more() ) { // loop on free face nodes
9605                 const SMDS_MeshNode* n =
9606                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9607                 if ( addedNodes.insert( n ).second )
9608                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9609               }
9610             }
9611             aBC /= addedNodes.size();
9612             double minDist = DBL_MAX;
9613             fIt = freeFaceList.begin();
9614             while ( fIt != freeFaceList.end() ) { // loop on free faces
9615               double dist = 0;
9616               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9617               while ( nodeIt->more() ) { // loop on free face nodes
9618                 const SMDS_MeshNode* n =
9619                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9620                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9621                 dist += ( aBC - p ).SquareModulus();
9622               }
9623               if ( dist < minDist ) {
9624                 minDist = dist;
9625                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9626               }
9627               else
9628                 fIt = freeFaceList.erase( fIt++ );
9629             }
9630           }
9631         } // choose one of several free faces of a volume
9632
9633         if ( freeFaceList.size() == 1 ) {
9634           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9635           faceSet->insert( aFreeFace );
9636           // complete a node set with nodes of a found free face
9637           //           for ( iNode = 0; iNode < ; iNode++ )
9638           //             nodeSet->insert( fNodes[ iNode ] );
9639         }
9640
9641       } // loop on volumes of a side
9642
9643       //       // complete a set of faces if new nodes in a nodeSet appeared
9644       //       // ----------------------------------------------------------
9645       //       if ( nodeSetSize != nodeSet->size() ) {
9646       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9647       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9648       //           while ( fIt->more() ) { // loop on faces sharing a node
9649       //             const SMDS_MeshElement* f = fIt->next();
9650       //             if ( faceSet->find( f ) == faceSet->end() ) {
9651       //               // check if all nodes are in nodeSet and
9652       //               // complete setOfFaceNodeSet if they are
9653       //               set <const SMDS_MeshNode*> faceNodeSet;
9654       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9655       //               bool allInSet = true;
9656       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9657       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9658       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9659       //                   allInSet = false;
9660       //                 else
9661       //                   faceNodeSet.insert( n );
9662       //               }
9663       //               if ( allInSet ) {
9664       //                 faceSet->insert( f );
9665       //                 setOfFaceNodeSet.insert( faceNodeSet );
9666       //               }
9667       //             }
9668       //           }
9669       //         }
9670       //       }
9671     } // Create temporary faces, if there are volumes given
9672   } // loop on sides
9673
9674   if ( faceSet1.size() != faceSet2.size() ) {
9675     // delete temporary faces: they are in reverseElements of actual nodes
9676     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9677     //    while ( tmpFaceIt->more() )
9678     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9679     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9680     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9681     //      aMesh->RemoveElement(*tmpFaceIt);
9682     MESSAGE("Diff nb of faces");
9683     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9684   }
9685
9686   // ============================================================
9687   // 2. Find nodes to merge:
9688   //              bind a node to remove to a node to put instead
9689   // ============================================================
9690
9691   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9692   if ( theFirstNode1 != theFirstNode2 )
9693     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9694   if ( theSecondNode1 != theSecondNode2 )
9695     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9696
9697   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9698   set< long > linkIdSet; // links to process
9699   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9700
9701   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9702   list< NLink > linkList[2];
9703   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9704   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9705   // loop on links in linkList; find faces by links and append links
9706   // of the found faces to linkList
9707   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9708   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9709   {
9710     NLink link[] = { *linkIt[0], *linkIt[1] };
9711     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9712     if ( !linkIdSet.count( linkID ) )
9713       continue;
9714
9715     // by links, find faces in the face sets,
9716     // and find indices of link nodes in the found faces;
9717     // in a face set, there is only one or no face sharing a link
9718     // ---------------------------------------------------------------
9719
9720     const SMDS_MeshElement* face[] = { 0, 0 };
9721     vector<const SMDS_MeshNode*> fnodes[2];
9722     int iLinkNode[2][2];
9723     TIDSortedElemSet avoidSet;
9724     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9725       const SMDS_MeshNode* n1 = link[iSide].first;
9726       const SMDS_MeshNode* n2 = link[iSide].second;
9727       //cout << "Side " << iSide << " ";
9728       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9729       // find a face by two link nodes
9730       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9731                                                       *faceSetPtr[ iSide ], avoidSet,
9732                                                       &iLinkNode[iSide][0],
9733                                                       &iLinkNode[iSide][1] );
9734       if ( face[ iSide ])
9735       {
9736         //cout << " F " << face[ iSide]->GetID() <<endl;
9737         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9738         // put face nodes to fnodes
9739         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9740         fnodes[ iSide ].assign( nIt, nEnd );
9741         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9742       }
9743     }
9744
9745     // check similarity of elements of the sides
9746     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9747       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9748       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9749         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9750       }
9751       else {
9752         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9753       }
9754       break; // do not return because it's necessary to remove tmp faces
9755     }
9756
9757     // set nodes to merge
9758     // -------------------
9759
9760     if ( face[0] && face[1] )  {
9761       const int nbNodes = face[0]->NbNodes();
9762       if ( nbNodes != face[1]->NbNodes() ) {
9763         MESSAGE("Diff nb of face nodes");
9764         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9765         break; // do not return because it s necessary to remove tmp faces
9766       }
9767       bool reverse[] = { false, false }; // order of nodes in the link
9768       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9769         // analyse link orientation in faces
9770         int i1 = iLinkNode[ iSide ][ 0 ];
9771         int i2 = iLinkNode[ iSide ][ 1 ];
9772         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9773       }
9774       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9775       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9776       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9777       {
9778         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9779                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9780       }
9781
9782       // add other links of the faces to linkList
9783       // -----------------------------------------
9784
9785       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9786         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9787         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9788         if ( !iter_isnew.second ) { // already in a set: no need to process
9789           linkIdSet.erase( iter_isnew.first );
9790         }
9791         else // new in set == encountered for the first time: add
9792         {
9793           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9794           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9795           linkList[0].push_back ( NLink( n1, n2 ));
9796           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9797         }
9798       }
9799     } // 2 faces found
9800
9801     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9802       break;
9803
9804   } // loop on link lists
9805
9806   if ( aResult == SEW_OK &&
9807        ( //linkIt[0] != linkList[0].end() ||
9808         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9809     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9810              " " << (faceSetPtr[1]->empty()));
9811     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9812   }
9813
9814   // ====================================================================
9815   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9816   // ====================================================================
9817
9818   // delete temporary faces
9819   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9820   //  while ( tmpFaceIt->more() )
9821   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9822   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9823   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9824     aMesh->RemoveElement(*tmpFaceIt);
9825
9826   if ( aResult != SEW_OK)
9827     return aResult;
9828
9829   list< int > nodeIDsToRemove;
9830   vector< const SMDS_MeshNode*> nodes;
9831   ElemFeatures elemType;
9832
9833   // loop on nodes replacement map
9834   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9835   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9836     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9837     {
9838       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9839       nodeIDsToRemove.push_back( nToRemove->GetID() );
9840       // loop on elements sharing nToRemove
9841       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9842       while ( invElemIt->more() ) {
9843         const SMDS_MeshElement* e = invElemIt->next();
9844         // get a new suite of nodes: make replacement
9845         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9846         nodes.resize( nbNodes );
9847         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9848         while ( nIt->more() ) {
9849           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9850           nnIt = nReplaceMap.find( n );
9851           if ( nnIt != nReplaceMap.end() ) {
9852             nbReplaced++;
9853             n = (*nnIt).second;
9854           }
9855           nodes[ i++ ] = n;
9856         }
9857         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9858         //         elemIDsToRemove.push_back( e->GetID() );
9859         //       else
9860         if ( nbReplaced )
9861         {
9862           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9863           aMesh->RemoveElement( e );
9864
9865           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9866           {
9867             AddToSameGroups( newElem, e, aMesh );
9868             if ( int aShapeId = e->getshapeId() )
9869               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9870           }
9871         }
9872       }
9873     }
9874
9875   Remove( nodeIDsToRemove, true );
9876
9877   return aResult;
9878 }
9879
9880 //================================================================================
9881 /*!
9882  * \brief Find corresponding nodes in two sets of faces
9883  * \param theSide1 - first face set
9884  * \param theSide2 - second first face
9885  * \param theFirstNode1 - a boundary node of set 1
9886  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9887  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9888  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9889  * \param nReplaceMap - output map of corresponding nodes
9890  * \return bool  - is a success or not
9891  */
9892 //================================================================================
9893
9894 #ifdef _DEBUG_
9895 //#define DEBUG_MATCHING_NODES
9896 #endif
9897
9898 SMESH_MeshEditor::Sew_Error
9899 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9900                                     set<const SMDS_MeshElement*>& theSide2,
9901                                     const SMDS_MeshNode*          theFirstNode1,
9902                                     const SMDS_MeshNode*          theFirstNode2,
9903                                     const SMDS_MeshNode*          theSecondNode1,
9904                                     const SMDS_MeshNode*          theSecondNode2,
9905                                     TNodeNodeMap &                nReplaceMap)
9906 {
9907   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9908
9909   nReplaceMap.clear();
9910   //if ( theFirstNode1 != theFirstNode2 )
9911   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9912   //if ( theSecondNode1 != theSecondNode2 )
9913   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9914
9915   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9916   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9917
9918   list< NLink > linkList[2];
9919   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9920   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9921
9922   // loop on links in linkList; find faces by links and append links
9923   // of the found faces to linkList
9924   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9925   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9926     NLink link[] = { *linkIt[0], *linkIt[1] };
9927     if ( linkSet.find( link[0] ) == linkSet.end() )
9928       continue;
9929
9930     // by links, find faces in the face sets,
9931     // and find indices of link nodes in the found faces;
9932     // in a face set, there is only one or no face sharing a link
9933     // ---------------------------------------------------------------
9934
9935     const SMDS_MeshElement* face[] = { 0, 0 };
9936     list<const SMDS_MeshNode*> notLinkNodes[2];
9937     //bool reverse[] = { false, false }; // order of notLinkNodes
9938     int nbNodes[2];
9939     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9940     {
9941       const SMDS_MeshNode* n1 = link[iSide].first;
9942       const SMDS_MeshNode* n2 = link[iSide].second;
9943       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9944       set< const SMDS_MeshElement* > facesOfNode1;
9945       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9946       {
9947         // during a loop of the first node, we find all faces around n1,
9948         // during a loop of the second node, we find one face sharing both n1 and n2
9949         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9950         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9951         while ( fIt->more() ) { // loop on faces sharing a node
9952           const SMDS_MeshElement* f = fIt->next();
9953           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9954               ! facesOfNode1.insert( f ).second ) // f encounters twice
9955           {
9956             if ( face[ iSide ] ) {
9957               MESSAGE( "2 faces per link " );
9958               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9959             }
9960             face[ iSide ] = f;
9961             faceSet->erase( f );
9962
9963             // get not link nodes
9964             int nbN = f->NbNodes();
9965             if ( f->IsQuadratic() )
9966               nbN /= 2;
9967             nbNodes[ iSide ] = nbN;
9968             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9969             int i1 = f->GetNodeIndex( n1 );
9970             int i2 = f->GetNodeIndex( n2 );
9971             int iEnd = nbN, iBeg = -1, iDelta = 1;
9972             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9973             if ( reverse ) {
9974               std::swap( iEnd, iBeg ); iDelta = -1;
9975             }
9976             int i = i2;
9977             while ( true ) {
9978               i += iDelta;
9979               if ( i == iEnd ) i = iBeg + iDelta;
9980               if ( i == i1 ) break;
9981               nodes.push_back ( f->GetNode( i ) );
9982             }
9983           }
9984         }
9985       }
9986     }
9987     // check similarity of elements of the sides
9988     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9989       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9990       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9991         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9992       }
9993       else {
9994         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9995       }
9996     }
9997
9998     // set nodes to merge
9999     // -------------------
10000
10001     if ( face[0] && face[1] )  {
10002       if ( nbNodes[0] != nbNodes[1] ) {
10003         MESSAGE("Diff nb of face nodes");
10004         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10005       }
10006 #ifdef DEBUG_MATCHING_NODES
10007       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10008                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10009                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10010 #endif
10011       int nbN = nbNodes[0];
10012       {
10013         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10014         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10015         for ( int i = 0 ; i < nbN - 2; ++i ) {
10016 #ifdef DEBUG_MATCHING_NODES
10017           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10018 #endif
10019           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10020         }
10021       }
10022
10023       // add other links of the face 1 to linkList
10024       // -----------------------------------------
10025
10026       const SMDS_MeshElement* f0 = face[0];
10027       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10028       for ( int i = 0; i < nbN; i++ )
10029       {
10030         const SMDS_MeshNode* n2 = f0->GetNode( i );
10031         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10032           linkSet.insert( SMESH_TLink( n1, n2 ));
10033         if ( !iter_isnew.second ) { // already in a set: no need to process
10034           linkSet.erase( iter_isnew.first );
10035         }
10036         else // new in set == encountered for the first time: add
10037         {
10038 #ifdef DEBUG_MATCHING_NODES
10039           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10040                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10041 #endif
10042           linkList[0].push_back ( NLink( n1, n2 ));
10043           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10044         }
10045         n1 = n2;
10046       }
10047     } // 2 faces found
10048   } // loop on link lists
10049
10050   return SEW_OK;
10051 }
10052
10053 namespace // automatically find theAffectedElems for DoubleNodes()
10054 {
10055   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10056
10057   //--------------------------------------------------------------------------------
10058   // Nodes shared by adjacent FissureBorder's.
10059   // 1 node  if FissureBorder separates faces
10060   // 2 nodes if FissureBorder separates volumes
10061   struct SubBorder
10062   {
10063     const SMDS_MeshNode* _nodes[2];
10064     int                  _nbNodes;
10065
10066     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10067     {
10068       _nodes[0] = n1;
10069       _nodes[1] = n2;
10070       _nbNodes = bool( n1 ) + bool( n2 );
10071       if ( _nbNodes == 2 && n1 > n2 )
10072         std::swap( _nodes[0], _nodes[1] );
10073     }
10074     bool operator<( const SubBorder& other ) const
10075     {
10076       for ( int i = 0; i < _nbNodes; ++i )
10077       {
10078         if ( _nodes[i] < other._nodes[i] ) return true;
10079         if ( _nodes[i] > other._nodes[i] ) return false;
10080       }
10081       return false;
10082     }
10083   };
10084
10085   //--------------------------------------------------------------------------------
10086   // Map a SubBorder to all FissureBorder it bounds
10087   struct FissureBorder;
10088   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10089   typedef TBorderLinks::iterator                               TMappedSub;
10090
10091   //--------------------------------------------------------------------------------
10092   /*!
10093    * \brief Element border (volume facet or face edge) at a fissure
10094    */
10095   struct FissureBorder
10096   {
10097     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10098     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10099
10100     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10101     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10102
10103     FissureBorder( FissureBorder && from ) // move constructor
10104     {
10105       std::swap( _nodes,       from._nodes );
10106       std::swap( _sortedNodes, from._sortedNodes );
10107       _elems[0] = from._elems[0];
10108       _elems[1] = from._elems[1];
10109     }
10110
10111     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10112                    std::vector< const SMDS_MeshElement* > & adjElems)
10113       : _nodes( elemToDuplicate->NbCornerNodes() )
10114     {
10115       for ( size_t i = 0; i < _nodes.size(); ++i )
10116         _nodes[i] = elemToDuplicate->GetNode( i );
10117
10118       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10119       findAdjacent( type, adjElems );
10120     }
10121
10122     FissureBorder( const SMDS_MeshNode**                    nodes,
10123                    const size_t                             nbNodes,
10124                    const SMDSAbs_ElementType                adjElemsType,
10125                    std::vector< const SMDS_MeshElement* > & adjElems)
10126       : _nodes( nodes, nodes + nbNodes )
10127     {
10128       findAdjacent( adjElemsType, adjElems );
10129     }
10130
10131     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10132                        std::vector< const SMDS_MeshElement* > & adjElems)
10133     {
10134       _elems[0] = _elems[1] = 0;
10135       adjElems.clear();
10136       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10137         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10138           _elems[i] = adjElems[i];
10139     }
10140
10141     bool operator<( const FissureBorder& other ) const
10142     {
10143       return GetSortedNodes() < other.GetSortedNodes();
10144     }
10145
10146     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10147     {
10148       if ( _sortedNodes.empty() && !_nodes.empty() )
10149       {
10150         FissureBorder* me = const_cast<FissureBorder*>( this );
10151         me->_sortedNodes = me->_nodes;
10152         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10153       }
10154       return _sortedNodes;
10155     }
10156
10157     size_t NbSub() const
10158     {
10159       return _nodes.size();
10160     }
10161
10162     SubBorder Sub(size_t i) const
10163     {
10164       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10165     }
10166
10167     void AddSelfTo( TBorderLinks& borderLinks )
10168     {
10169       _mappedSubs.resize( NbSub() );
10170       for ( size_t i = 0; i < NbSub(); ++i )
10171       {
10172         TBorderLinks::iterator s2b =
10173           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10174         s2b->second.push_back( this );
10175         _mappedSubs[ i ] = s2b;
10176       }
10177     }
10178
10179     void Clear()
10180     {
10181       _nodes.clear();
10182     }
10183
10184     const SMDS_MeshElement* GetMarkedElem() const
10185     {
10186       if ( _nodes.empty() ) return 0; // cleared
10187       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10188       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10189       return 0;
10190     }
10191
10192     gp_XYZ GetNorm() const // normal to the border
10193     {
10194       gp_XYZ norm;
10195       if ( _nodes.size() == 2 )
10196       {
10197         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10198         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10199           avgNorm += norm;
10200         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10201           avgNorm += norm;
10202
10203         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10204         norm = bordDir ^ avgNorm;
10205       }
10206       else
10207       {
10208         SMESH_NodeXYZ p0( _nodes[0] );
10209         SMESH_NodeXYZ p1( _nodes[1] );
10210         SMESH_NodeXYZ p2( _nodes[2] );
10211         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10212       }
10213       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10214         norm.Reverse();
10215
10216       return norm;
10217     }
10218
10219     void ChooseSide() // mark an _elem located at positive side of fissure
10220     {
10221       _elems[0]->setIsMarked( true );
10222       gp_XYZ norm = GetNorm();
10223       double maxX = norm.Coord(1);
10224       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10225       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10226       if ( maxX < 0 )
10227       {
10228         _elems[0]->setIsMarked( false );
10229         _elems[1]->setIsMarked( true );
10230       }
10231     }
10232
10233   }; // struct FissureBorder
10234
10235   //--------------------------------------------------------------------------------
10236   /*!
10237    * \brief Classifier of elements at fissure edge
10238    */
10239   class FissureNormal
10240   {
10241     std::vector< gp_XYZ > _normals;
10242     bool                  _bothIn;
10243
10244   public:
10245     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10246     {
10247       _bothIn = false;
10248       _normals.reserve(2);
10249       _normals.push_back( bord.GetNorm() );
10250       if ( _normals.size() == 2 )
10251         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10252     }
10253
10254     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10255     {
10256       bool isIn = false;
10257       switch ( _normals.size() ) {
10258       case 1:
10259       {
10260         isIn = !isOut( n, _normals[0], elem );
10261         break;
10262       }
10263       case 2:
10264       {
10265         bool in1 = !isOut( n, _normals[0], elem );
10266         bool in2 = !isOut( n, _normals[1], elem );
10267         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10268       }
10269       }
10270       return isIn;
10271     }
10272   };
10273
10274   //================================================================================
10275   /*!
10276    * \brief Classify an element by a plane passing through a node
10277    */
10278   //================================================================================
10279
10280   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10281   {
10282     SMESH_NodeXYZ p = n;
10283     double sumDot = 0;
10284     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10285     {
10286       SMESH_NodeXYZ pi = elem->GetNode( i );
10287       sumDot += norm * ( pi - p );
10288     }
10289     return sumDot < -1e-100;
10290   }
10291
10292   //================================================================================
10293   /*!
10294    * \brief Find FissureBorder's by nodes to duplicate
10295    */
10296   //================================================================================
10297
10298   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10299                            std::vector< FissureBorder > & theFissureBorders )
10300   {
10301     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10302     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10303     if ( !n ) return;
10304     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10305     if ( n->NbInverseElements( elemType ) == 0 )
10306     {
10307       elemType = SMDSAbs_Face;
10308       if ( n->NbInverseElements( elemType ) == 0 )
10309         return;
10310     }
10311     // unmark elements touching the fissure
10312     for ( ; nIt != theNodes.end(); ++nIt )
10313       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10314
10315     // loop on elements touching the fissure to get their borders belonging to the fissure
10316     std::set< FissureBorder >              fissureBorders;
10317     std::vector< const SMDS_MeshElement* > adjElems;
10318     std::vector< const SMDS_MeshNode* >    nodes;
10319     SMDS_VolumeTool volTool;
10320     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10321     {
10322       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10323       while ( invIt->more() )
10324       {
10325         const SMDS_MeshElement* eInv = invIt->next();
10326         if ( eInv->isMarked() ) continue;
10327         eInv->setIsMarked( true );
10328
10329         if ( elemType == SMDSAbs_Volume )
10330         {
10331           volTool.Set( eInv );
10332           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10333           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10334           {
10335             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10336             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10337             nodes.clear();
10338             bool allOnFissure = true;
10339             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10340               if (( allOnFissure = theNodes.count( nn[ iN ])))
10341                 nodes.push_back( nn[ iN ]);
10342             if ( allOnFissure )
10343               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10344                                                                elemType, adjElems )));
10345           }
10346         }
10347         else // elemType == SMDSAbs_Face
10348         {
10349           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10350           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10351           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10352           {
10353             nn[1]      = eInv->GetNode( iN );
10354             onFissure1 = theNodes.count( nn[1] );
10355             if ( onFissure0 && onFissure1 )
10356               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10357             nn[0]      = nn[1];
10358             onFissure0 = onFissure1;
10359           }
10360         }
10361       }
10362     }
10363
10364     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10365     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10366     for ( ; bord != fissureBorders.end(); ++bord )
10367     {
10368       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10369     }
10370     return;
10371   } // findFissureBorders()
10372
10373   //================================================================================
10374   /*!
10375    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10376    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10377    *  \param [in] theNodesNot - nodes not to duplicate
10378    *  \param [out] theAffectedElems - the found elements
10379    */
10380   //================================================================================
10381
10382   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10383                           TIDSortedElemSet&       theAffectedElems)
10384   {
10385     if ( theElemsOrNodes.empty() ) return;
10386
10387     // find FissureBorder's
10388
10389     std::vector< FissureBorder >           fissure;
10390     std::vector< const SMDS_MeshElement* > elemsByFacet;
10391
10392     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10393     if ( (*elIt)->GetType() == SMDSAbs_Node )
10394     {
10395       findFissureBorders( theElemsOrNodes, fissure );
10396     }
10397     else
10398     {
10399       fissure.reserve( theElemsOrNodes.size() );
10400       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10401         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10402     }
10403     if ( fissure.empty() )
10404       return;
10405
10406     // fill borderLinks
10407
10408     TBorderLinks borderLinks;
10409
10410     for ( size_t i = 0; i < fissure.size(); ++i )
10411     {
10412       fissure[i].AddSelfTo( borderLinks );
10413     }
10414
10415     // get theAffectedElems
10416
10417     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10418     for ( size_t i = 0; i < fissure.size(); ++i )
10419       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10420       {
10421         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10422                                         false, /*markElem=*/true );
10423       }
10424
10425     std::vector<const SMDS_MeshNode *>                 facetNodes;
10426     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10427     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10428
10429     // choose a side of fissure
10430     fissure[0].ChooseSide();
10431     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10432
10433     size_t nbCheckedBorders = 0;
10434     while ( nbCheckedBorders < fissure.size() )
10435     {
10436       // find a FissureBorder to treat
10437       FissureBorder* bord = 0;
10438       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10439         if ( fissure[i].GetMarkedElem() )
10440           bord = & fissure[i];
10441       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10442         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10443         {
10444           bord = & fissure[i];
10445           bord->ChooseSide();
10446           theAffectedElems.insert( bord->GetMarkedElem() );
10447         }
10448       if ( !bord ) return;
10449       ++nbCheckedBorders;
10450
10451       // treat FissureBorder's linked to bord
10452       fissureNodes.clear();
10453       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10454       for ( size_t i = 0; i < bord->NbSub(); ++i )
10455       {
10456         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10457         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10458         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10459         const SubBorder&                          sb = l2b->first;
10460         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10461
10462         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10463         {
10464           for ( int j = 0; j < sb._nbNodes; ++j )
10465             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10466           continue;
10467         }
10468
10469         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10470         // until an elem adjacent to a neighbour FissureBorder is found
10471         facetNodes.clear();
10472         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10473         facetNodes.resize( sb._nbNodes + 1 );
10474
10475         while ( bordElem )
10476         {
10477           // check if bordElem is adjacent to a neighbour FissureBorder
10478           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10479           {
10480             FissureBorder* bord2 = linkedBorders[j];
10481             if ( bord2 == bord ) continue;
10482             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10483               bordElem = 0;
10484             else
10485               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10486           }
10487           if ( !bordElem )
10488             break;
10489
10490           // find the next bordElem
10491           const SMDS_MeshElement* nextBordElem = 0;
10492           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10493           {
10494             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10495             if ( fissureNodes.count( n )) continue;
10496
10497             facetNodes[ sb._nbNodes ] = n;
10498             elemsByFacet.clear();
10499             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10500             {
10501               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10502                 if ( elemsByFacet[ iE ] != bordElem &&
10503                      !elemsByFacet[ iE ]->isMarked() )
10504                 {
10505                   theAffectedElems.insert( elemsByFacet[ iE ]);
10506                   elemsByFacet[ iE ]->setIsMarked( true );
10507                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10508                     nextBordElem = elemsByFacet[ iE ];
10509                 }
10510             }
10511           }
10512           bordElem = nextBordElem;
10513
10514         } // while ( bordElem )
10515
10516         linkedBorders.clear(); // not to treat this link any more
10517
10518       } // loop on SubBorder's of a FissureBorder
10519
10520       bord->Clear();
10521
10522     } // loop on FissureBorder's
10523
10524
10525     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10526
10527     // mark nodes of theAffectedElems
10528     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10529
10530     // unmark nodes of the fissure
10531     elIt = theElemsOrNodes.begin();
10532     if ( (*elIt)->GetType() == SMDSAbs_Node )
10533       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10534     else
10535       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10536
10537     std::vector< gp_XYZ > normVec;
10538
10539     // loop on nodes of the fissure, add elements having marked nodes
10540     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10541     {
10542       const SMDS_MeshElement* e = (*elIt);
10543       if ( e->GetType() != SMDSAbs_Node )
10544         e->setIsMarked( true ); // avoid adding a fissure element
10545
10546       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10547       {
10548         const SMDS_MeshNode* n = e->GetNode( iN );
10549         if ( fissEdgeNodes2Norm.count( n ))
10550           continue;
10551
10552         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10553         while ( invIt->more() )
10554         {
10555           const SMDS_MeshElement* eInv = invIt->next();
10556           if ( eInv->isMarked() ) continue;
10557           eInv->setIsMarked( true );
10558
10559           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10560           while( nIt->more() )
10561             if ( nIt->next()->isMarked())
10562             {
10563               theAffectedElems.insert( eInv );
10564               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10565               n->setIsMarked( false );
10566               break;
10567             }
10568         }
10569       }
10570     }
10571
10572     // add elements on the fissure edge
10573     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10574     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10575     {
10576       const SMDS_MeshNode* edgeNode = n2N->first;
10577       const FissureNormal & normals = n2N->second;
10578
10579       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10580       while ( invIt->more() )
10581       {
10582         const SMDS_MeshElement* eInv = invIt->next();
10583         if ( eInv->isMarked() ) continue;
10584         eInv->setIsMarked( true );
10585
10586         // classify eInv using normals
10587         bool toAdd = normals.IsIn( edgeNode, eInv );
10588         if ( toAdd ) // check if all nodes lie on the fissure edge
10589         {
10590           bool notOnEdge = false;
10591           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10592             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10593           toAdd = notOnEdge;
10594         }
10595         if ( toAdd )
10596         {
10597           theAffectedElems.insert( eInv );
10598         }
10599       }
10600     }
10601
10602     return;
10603   } // findAffectedElems()
10604 } // namespace
10605
10606 //================================================================================
10607 /*!
10608  * \brief Create elements equal (on same nodes) to given ones
10609  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10610  *              elements of the uppest dimension are duplicated.
10611  */
10612 //================================================================================
10613
10614 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10615 {
10616   ClearLastCreated();
10617   SMESHDS_Mesh* mesh = GetMeshDS();
10618
10619   // get an element type and an iterator over elements
10620
10621   SMDSAbs_ElementType type = SMDSAbs_All;
10622   SMDS_ElemIteratorPtr elemIt;
10623   if ( theElements.empty() )
10624   {
10625     if ( mesh->NbNodes() == 0 )
10626       return;
10627     // get most complex type
10628     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10629       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10630       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10631     };
10632     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10633       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10634       {
10635         type = types[i];
10636         elemIt = mesh->elementsIterator( type );
10637         break;
10638       }
10639   }
10640   else
10641   {
10642     //type = (*theElements.begin())->GetType();
10643     elemIt = SMESHUtils::elemSetIterator( theElements );
10644   }
10645
10646   // un-mark all elements to avoid duplicating just created elements
10647   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10648
10649   // duplicate elements
10650
10651   ElemFeatures elemType;
10652
10653   vector< const SMDS_MeshNode* > nodes;
10654   while ( elemIt->more() )
10655   {
10656     const SMDS_MeshElement* elem = elemIt->next();
10657     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10658         ( elem->isMarked() ))
10659       continue;
10660
10661     elemType.Init( elem, /*basicOnly=*/false );
10662     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10663
10664     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10665       newElem->setIsMarked( true );
10666   }
10667 }
10668
10669 //================================================================================
10670 /*!
10671   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10672   \param theElems - the list of elements (edges or faces) to be replicated
10673   The nodes for duplication could be found from these elements
10674   \param theNodesNot - list of nodes to NOT replicate
10675   \param theAffectedElems - the list of elements (cells and edges) to which the
10676   replicated nodes should be associated to.
10677   \return TRUE if operation has been completed successfully, FALSE otherwise
10678 */
10679 //================================================================================
10680
10681 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10682                                     const TIDSortedElemSet& theNodesNot,
10683                                     const TIDSortedElemSet& theAffectedElems )
10684 {
10685   ClearLastCreated();
10686
10687   if ( theElems.size() == 0 )
10688     return false;
10689
10690   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10691   if ( !aMeshDS )
10692     return false;
10693
10694   bool res = false;
10695   TNodeNodeMap anOldNodeToNewNode;
10696   // duplicate elements and nodes
10697   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10698   // replce nodes by duplications
10699   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10700   return res;
10701 }
10702
10703 //================================================================================
10704 /*!
10705   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10706   \param theMeshDS - mesh instance
10707   \param theElems - the elements replicated or modified (nodes should be changed)
10708   \param theNodesNot - nodes to NOT replicate
10709   \param theNodeNodeMap - relation of old node to new created node
10710   \param theIsDoubleElem - flag os to replicate element or modify
10711   \return TRUE if operation has been completed successfully, FALSE otherwise
10712 */
10713 //================================================================================
10714
10715 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10716                                    const TIDSortedElemSet& theElems,
10717                                    const TIDSortedElemSet& theNodesNot,
10718                                    TNodeNodeMap&           theNodeNodeMap,
10719                                    const bool              theIsDoubleElem )
10720 {
10721   // iterate through element and duplicate them (by nodes duplication)
10722   bool res = false;
10723   std::vector<const SMDS_MeshNode*> newNodes;
10724   ElemFeatures elemType;
10725
10726   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10727   for ( ;  elemItr != theElems.end(); ++elemItr )
10728   {
10729     const SMDS_MeshElement* anElem = *elemItr;
10730     // if (!anElem)
10731     //   continue;
10732
10733     // duplicate nodes to duplicate element
10734     bool isDuplicate = false;
10735     newNodes.resize( anElem->NbNodes() );
10736     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10737     int ind = 0;
10738     while ( anIter->more() )
10739     {
10740       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10741       const SMDS_MeshNode*  aNewNode = aCurrNode;
10742       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10743       if ( n2n != theNodeNodeMap.end() )
10744       {
10745         aNewNode = n2n->second;
10746       }
10747       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10748       {
10749         // duplicate node
10750         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10751         copyPosition( aCurrNode, aNewNode );
10752         theNodeNodeMap[ aCurrNode ] = aNewNode;
10753         myLastCreatedNodes.push_back( aNewNode );
10754       }
10755       isDuplicate |= (aCurrNode != aNewNode);
10756       newNodes[ ind++ ] = aNewNode;
10757     }
10758     if ( !isDuplicate )
10759       continue;
10760
10761     if ( theIsDoubleElem )
10762       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10763     else
10764       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10765
10766     res = true;
10767   }
10768   return res;
10769 }
10770
10771 //================================================================================
10772 /*!
10773   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10774   \param theNodes - identifiers of nodes to be doubled
10775   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10776   nodes. If list of element identifiers is empty then nodes are doubled but
10777   they not assigned to elements
10778   \return TRUE if operation has been completed successfully, FALSE otherwise
10779 */
10780 //================================================================================
10781
10782 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10783                                     const std::list< int >& theListOfModifiedElems )
10784 {
10785   ClearLastCreated();
10786
10787   if ( theListOfNodes.size() == 0 )
10788     return false;
10789
10790   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10791   if ( !aMeshDS )
10792     return false;
10793
10794   // iterate through nodes and duplicate them
10795
10796   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10797
10798   std::list< int >::const_iterator aNodeIter;
10799   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10800   {
10801     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10802     if ( !aNode )
10803       continue;
10804
10805     // duplicate node
10806
10807     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10808     if ( aNewNode )
10809     {
10810       copyPosition( aNode, aNewNode );
10811       anOldNodeToNewNode[ aNode ] = aNewNode;
10812       myLastCreatedNodes.push_back( aNewNode );
10813     }
10814   }
10815
10816   // Change nodes of elements
10817
10818   std::vector<const SMDS_MeshNode*> aNodeArr;
10819
10820   std::list< int >::const_iterator anElemIter;
10821   for ( anElemIter =  theListOfModifiedElems.begin();
10822         anElemIter != theListOfModifiedElems.end();
10823         anElemIter++ )
10824   {
10825     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10826     if ( !anElem )
10827       continue;
10828
10829     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10830     for( size_t i = 0; i < aNodeArr.size(); ++i )
10831     {
10832       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10833         anOldNodeToNewNode.find( aNodeArr[ i ]);
10834       if ( n2n != anOldNodeToNewNode.end() )
10835         aNodeArr[ i ] = n2n->second;
10836     }
10837     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10838   }
10839
10840   return true;
10841 }
10842
10843 namespace {
10844
10845   //================================================================================
10846   /*!
10847     \brief Check if element located inside shape
10848     \return TRUE if IN or ON shape, FALSE otherwise
10849   */
10850   //================================================================================
10851
10852   template<class Classifier>
10853   bool isInside(const SMDS_MeshElement* theElem,
10854                 Classifier&             theClassifier,
10855                 const double            theTol)
10856   {
10857     gp_XYZ centerXYZ (0, 0, 0);
10858     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10859       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10860
10861     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10862     theClassifier.Perform(aPnt, theTol);
10863     TopAbs_State aState = theClassifier.State();
10864     return (aState == TopAbs_IN || aState == TopAbs_ON );
10865   }
10866
10867   //================================================================================
10868   /*!
10869    * \brief Classifier of the 3D point on the TopoDS_Face
10870    *        with interaface suitable for isInside()
10871    */
10872   //================================================================================
10873
10874   struct _FaceClassifier
10875   {
10876     Extrema_ExtPS       _extremum;
10877     BRepAdaptor_Surface _surface;
10878     TopAbs_State        _state;
10879
10880     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10881     {
10882       _extremum.Initialize( _surface,
10883                             _surface.FirstUParameter(), _surface.LastUParameter(),
10884                             _surface.FirstVParameter(), _surface.LastVParameter(),
10885                             _surface.Tolerance(), _surface.Tolerance() );
10886     }
10887     void Perform(const gp_Pnt& aPnt, double theTol)
10888     {
10889       theTol *= theTol;
10890       _state = TopAbs_OUT;
10891       _extremum.Perform(aPnt);
10892       if ( _extremum.IsDone() )
10893         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10894           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10895     }
10896     TopAbs_State State() const
10897     {
10898       return _state;
10899     }
10900   };
10901 }
10902
10903 //================================================================================
10904 /*!
10905   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10906   This method is the first step of DoubleNodeElemGroupsInRegion.
10907   \param theElems - list of groups of elements (edges or faces) to be replicated
10908   \param theNodesNot - list of groups of nodes not to replicated
10909   \param theShape - shape to detect affected elements (element which geometric center
10910          located on or inside shape). If the shape is null, detection is done on faces orientations
10911          (select elements with a gravity center on the side given by faces normals).
10912          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10913          The replicated nodes should be associated to affected elements.
10914   \return true
10915   \sa DoubleNodeElemGroupsInRegion()
10916 */
10917 //================================================================================
10918
10919 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10920                                                    const TIDSortedElemSet& theNodesNot,
10921                                                    const TopoDS_Shape&     theShape,
10922                                                    TIDSortedElemSet&       theAffectedElems)
10923 {
10924   if ( theShape.IsNull() )
10925   {
10926     findAffectedElems( theElems, theAffectedElems );
10927   }
10928   else
10929   {
10930     const double aTol = Precision::Confusion();
10931     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10932     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10933     if ( theShape.ShapeType() == TopAbs_SOLID )
10934     {
10935       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10936       bsc3d->PerformInfinitePoint(aTol);
10937     }
10938     else if (theShape.ShapeType() == TopAbs_FACE )
10939     {
10940       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10941     }
10942
10943     // iterates on indicated elements and get elements by back references from their nodes
10944     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10945     for ( ;  elemItr != theElems.end(); ++elemItr )
10946     {
10947       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10948       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10949       while ( nodeItr->more() )
10950       {
10951         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10952         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10953           continue;
10954         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10955         while ( backElemItr->more() )
10956         {
10957           const SMDS_MeshElement* curElem = backElemItr->next();
10958           if ( curElem && theElems.find(curElem) == theElems.end() &&
10959                ( bsc3d.get() ?
10960                  isInside( curElem, *bsc3d, aTol ) :
10961                  isInside( curElem, *aFaceClassifier, aTol )))
10962             theAffectedElems.insert( curElem );
10963         }
10964       }
10965     }
10966   }
10967   return true;
10968 }
10969
10970 //================================================================================
10971 /*!
10972   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10973   \param theElems - group of of elements (edges or faces) to be replicated
10974   \param theNodesNot - group of nodes not to replicate
10975   \param theShape - shape to detect affected elements (element which geometric center
10976   located on or inside shape).
10977   The replicated nodes should be associated to affected elements.
10978   \return TRUE if operation has been completed successfully, FALSE otherwise
10979 */
10980 //================================================================================
10981
10982 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10983                                             const TIDSortedElemSet& theNodesNot,
10984                                             const TopoDS_Shape&     theShape )
10985 {
10986   if ( theShape.IsNull() )
10987     return false;
10988
10989   const double aTol = Precision::Confusion();
10990   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10991   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10992   if ( theShape.ShapeType() == TopAbs_SOLID )
10993   {
10994     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10995     bsc3d->PerformInfinitePoint(aTol);
10996   }
10997   else if (theShape.ShapeType() == TopAbs_FACE )
10998   {
10999     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11000   }
11001
11002   // iterates on indicated elements and get elements by back references from their nodes
11003   TIDSortedElemSet anAffected;
11004   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11005   for ( ;  elemItr != theElems.end(); ++elemItr )
11006   {
11007     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11008     if (!anElem)
11009       continue;
11010
11011     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11012     while ( nodeItr->more() )
11013     {
11014       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11015       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11016         continue;
11017       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11018       while ( backElemItr->more() )
11019       {
11020         const SMDS_MeshElement* curElem = backElemItr->next();
11021         if ( curElem && theElems.find(curElem) == theElems.end() &&
11022              ( bsc3d ?
11023                isInside( curElem, *bsc3d, aTol ) :
11024                isInside( curElem, *aFaceClassifier, aTol )))
11025           anAffected.insert( curElem );
11026       }
11027     }
11028   }
11029   return DoubleNodes( theElems, theNodesNot, anAffected );
11030 }
11031
11032 /*!
11033  *  \brief compute an oriented angle between two planes defined by four points.
11034  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11035  *  @param p0 base of the rotation axe
11036  *  @param p1 extremity of the rotation axe
11037  *  @param g1 belongs to the first plane
11038  *  @param g2 belongs to the second plane
11039  */
11040 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11041 {
11042   gp_Vec vref(p0, p1);
11043   gp_Vec v1(p0, g1);
11044   gp_Vec v2(p0, g2);
11045   gp_Vec n1 = vref.Crossed(v1);
11046   gp_Vec n2 = vref.Crossed(v2);
11047   try {
11048     return n2.AngleWithRef(n1, vref);
11049   }
11050   catch ( Standard_Failure& ) {
11051   }
11052   return Max( v1.Magnitude(), v2.Magnitude() );
11053 }
11054
11055 /*!
11056  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11057  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11058  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11059  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11060  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11061  * 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.
11062  * 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.
11063  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11064  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11065  * \param theElems - list of groups of volumes, where a group of volume is a set of
11066  *        SMDS_MeshElements sorted by Id.
11067  * \param createJointElems - if TRUE, create the elements
11068  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11069  *        the boundary between \a theDomains and the rest mesh
11070  * \return TRUE if operation has been completed successfully, FALSE otherwise
11071  */
11072 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11073                                                      bool                                 createJointElems,
11074                                                      bool                                 onAllBoundaries)
11075 {
11076   // MESSAGE("----------------------------------------------");
11077   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11078   // MESSAGE("----------------------------------------------");
11079
11080   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11081   meshDS->BuildDownWardConnectivity(true);
11082   CHRONO(50);
11083   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11084
11085   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11086   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11087   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11088
11089   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11090   std::map<int,int>celldom; // cell vtkId --> domain
11091   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11092   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11093   faceDomains.clear();
11094   celldom.clear();
11095   cellDomains.clear();
11096   nodeDomains.clear();
11097   std::map<int,int> emptyMap;
11098   std::set<int> emptySet;
11099   emptyMap.clear();
11100
11101   //MESSAGE(".. Number of domains :"<<theElems.size());
11102
11103   TIDSortedElemSet theRestDomElems;
11104   const int iRestDom  = -1;
11105   const int idom0     = onAllBoundaries ? iRestDom : 0;
11106   const int nbDomains = theElems.size();
11107
11108   // Check if the domains do not share an element
11109   for (int idom = 0; idom < nbDomains-1; idom++)
11110   {
11111     //       MESSAGE("... Check of domain #" << idom);
11112     const TIDSortedElemSet& domain = theElems[idom];
11113     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11114     for (; elemItr != domain.end(); ++elemItr)
11115     {
11116       const SMDS_MeshElement* anElem = *elemItr;
11117       int idombisdeb = idom + 1 ;
11118       // check if the element belongs to a domain further in the list
11119       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11120       {
11121         const TIDSortedElemSet& domainbis = theElems[idombis];
11122         if ( domainbis.count( anElem ))
11123         {
11124           MESSAGE(".... Domain #" << idom);
11125           MESSAGE(".... Domain #" << idombis);
11126           throw SALOME_Exception("The domains are not disjoint.");
11127           return false ;
11128         }
11129       }
11130     }
11131   }
11132
11133   for (int idom = 0; idom < nbDomains; idom++)
11134   {
11135
11136     // --- build a map (face to duplicate --> volume to modify)
11137     //     with all the faces shared by 2 domains (group of elements)
11138     //     and corresponding volume of this domain, for each shared face.
11139     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11140
11141     //MESSAGE("... Neighbors of domain #" << idom);
11142     const TIDSortedElemSet& domain = theElems[idom];
11143     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11144     for (; elemItr != domain.end(); ++elemItr)
11145     {
11146       const SMDS_MeshElement* anElem = *elemItr;
11147       if (!anElem)
11148         continue;
11149       int vtkId = anElem->GetVtkID();
11150       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11151       int neighborsVtkIds[NBMAXNEIGHBORS];
11152       int downIds[NBMAXNEIGHBORS];
11153       unsigned char downTypes[NBMAXNEIGHBORS];
11154       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11155       for (int n = 0; n < nbNeighbors; n++)
11156       {
11157         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11158         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11159         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11160         {
11161           bool ok = false;
11162           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11163           {
11164             // MESSAGE("Domain " << idombis);
11165             const TIDSortedElemSet& domainbis = theElems[idombis];
11166             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11167           }
11168           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11169           {
11170             DownIdType face(downIds[n], downTypes[n]);
11171             if (!faceDomains[face].count(idom))
11172             {
11173               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11174               celldom[vtkId] = idom;
11175               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11176             }
11177             if ( !ok )
11178             {
11179               theRestDomElems.insert( elem );
11180               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11181               celldom[neighborsVtkIds[n]] = iRestDom;
11182             }
11183           }
11184         }
11185       }
11186     }
11187   }
11188
11189   //MESSAGE("Number of shared faces " << faceDomains.size());
11190   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11191
11192   // --- explore the shared faces domain by domain,
11193   //     explore the nodes of the face and see if they belong to a cell in the domain,
11194   //     which has only a node or an edge on the border (not a shared face)
11195
11196   for (int idomain = idom0; idomain < nbDomains; idomain++)
11197   {
11198     //MESSAGE("Domain " << idomain);
11199     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11200     itface = faceDomains.begin();
11201     for (; itface != faceDomains.end(); ++itface)
11202     {
11203       const std::map<int, int>& domvol = itface->second;
11204       if (!domvol.count(idomain))
11205         continue;
11206       DownIdType face = itface->first;
11207       //MESSAGE(" --- face " << face.cellId);
11208       std::set<int> oldNodes;
11209       oldNodes.clear();
11210       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11211       std::set<int>::iterator itn = oldNodes.begin();
11212       for (; itn != oldNodes.end(); ++itn)
11213       {
11214         int oldId = *itn;
11215         //MESSAGE("     node " << oldId);
11216         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11217         for (int i=0; i<l.ncells; i++)
11218         {
11219           int vtkId = l.cells[i];
11220           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11221           if (!domain.count(anElem))
11222             continue;
11223           int vtkType = grid->GetCellType(vtkId);
11224           int downId = grid->CellIdToDownId(vtkId);
11225           if (downId < 0)
11226           {
11227             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11228             continue; // not OK at this stage of the algorithm:
11229             //no cells created after BuildDownWardConnectivity
11230           }
11231           DownIdType aCell(downId, vtkType);
11232           cellDomains[aCell][idomain] = vtkId;
11233           celldom[vtkId] = idomain;
11234           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11235         }
11236       }
11237     }
11238   }
11239
11240   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11241   //     for each shared face, get the nodes
11242   //     for each node, for each domain of the face, create a clone of the node
11243
11244   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11245   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11246   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11247
11248   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11249   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11250   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11251
11252   //MESSAGE(".. Duplication of the nodes");
11253   for (int idomain = idom0; idomain < nbDomains; idomain++)
11254   {
11255     itface = faceDomains.begin();
11256     for (; itface != faceDomains.end(); ++itface)
11257     {
11258       const std::map<int, int>& domvol = itface->second;
11259       if (!domvol.count(idomain))
11260         continue;
11261       DownIdType face = itface->first;
11262       //MESSAGE(" --- face " << face.cellId);
11263       std::set<int> oldNodes;
11264       oldNodes.clear();
11265       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11266       std::set<int>::iterator itn = oldNodes.begin();
11267       for (; itn != oldNodes.end(); ++itn)
11268       {
11269         int oldId = *itn;
11270         if (nodeDomains[oldId].empty())
11271         {
11272           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11273           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11274         }
11275         std::map<int, int>::const_iterator itdom = domvol.begin();
11276         for (; itdom != domvol.end(); ++itdom)
11277         {
11278           int idom = itdom->first;
11279           //MESSAGE("         domain " << idom);
11280           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11281           {
11282             if (nodeDomains[oldId].size() >= 2) // a multiple node
11283             {
11284               vector<int> orderedDoms;
11285               //MESSAGE("multiple node " << oldId);
11286               if (mutipleNodes.count(oldId))
11287                 orderedDoms = mutipleNodes[oldId];
11288               else
11289               {
11290                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11291                 for (; it != nodeDomains[oldId].end(); ++it)
11292                   orderedDoms.push_back(it->first);
11293               }
11294               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11295               //stringstream txt;
11296               //for (int i=0; i<orderedDoms.size(); i++)
11297               //  txt << orderedDoms[i] << " ";
11298               //MESSAGE("orderedDoms " << txt.str());
11299               mutipleNodes[oldId] = orderedDoms;
11300             }
11301             double *coords = grid->GetPoint(oldId);
11302             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11303             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11304             int newId = newNode->GetVtkID();
11305             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11306             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11307           }
11308         }
11309       }
11310     }
11311   }
11312
11313   //MESSAGE(".. Creation of elements");
11314   for (int idomain = idom0; idomain < nbDomains; idomain++)
11315   {
11316     itface = faceDomains.begin();
11317     for (; itface != faceDomains.end(); ++itface)
11318     {
11319       std::map<int, int> domvol = itface->second;
11320       if (!domvol.count(idomain))
11321         continue;
11322       DownIdType face = itface->first;
11323       //MESSAGE(" --- face " << face.cellId);
11324       std::set<int> oldNodes;
11325       oldNodes.clear();
11326       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11327       int nbMultipleNodes = 0;
11328       std::set<int>::iterator itn = oldNodes.begin();
11329       for (; itn != oldNodes.end(); ++itn)
11330       {
11331         int oldId = *itn;
11332         if (mutipleNodes.count(oldId))
11333           nbMultipleNodes++;
11334       }
11335       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11336       {
11337         //MESSAGE("multiple Nodes detected on a shared face");
11338         int downId = itface->first.cellId;
11339         unsigned char cellType = itface->first.cellType;
11340         // --- shared edge or shared face ?
11341         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11342         {
11343           int nodes[3];
11344           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11345           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11346             if (mutipleNodes.count(nodes[i]))
11347               if (!mutipleNodesToFace.count(nodes[i]))
11348                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11349         }
11350         else // shared face (between two volumes)
11351         {
11352           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11353           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11354           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11355           for (int ie =0; ie < nbEdges; ie++)
11356           {
11357             int nodes[3];
11358             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11359             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11360             {
11361               vector<int> vn0 = mutipleNodes[nodes[0]];
11362               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11363               vector<int> doms;
11364               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11365                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11366                   if ( vn0[i0] == vn1[i1] )
11367                     doms.push_back( vn0[ i0 ]);
11368               if ( doms.size() > 2 )
11369               {
11370                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11371                 double *coords = grid->GetPoint(nodes[0]);
11372                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11373                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11374                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11375                 gp_Pnt gref;
11376                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11377                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11378                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11379                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11380                 for ( size_t id = 0; id < doms.size(); id++ )
11381                 {
11382                   int idom = doms[id];
11383                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11384                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11385                   {
11386                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11387                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11388                     if (domain.count(elem))
11389                     {
11390                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11391                       domvol[idom] = (SMDS_MeshVolume*) svol;
11392                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11393                       double values[3] = { 0,0,0 };
11394                       vtkIdType npts = 0;
11395                       vtkIdType const *pts(nullptr);
11396                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11397                       for ( vtkIdType i = 0; i < npts; ++i )
11398                       {
11399                         double *coords = grid->GetPoint( pts[i] );
11400                         for ( int j = 0; j < 3; ++j )
11401                           values[j] += coords[j] / npts;
11402                       }
11403                       if ( id == 0 )
11404                       {
11405                         gref.SetCoord( values[0], values[1], values[2] );
11406                         angleDom[idom] = 0;
11407                       }
11408                       else
11409                       {
11410                         gp_Pnt g( values[0], values[1], values[2] );
11411                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11412                         //MESSAGE("  angle=" << angleDom[idom]);
11413                       }
11414                       break;
11415                     }
11416                   }
11417                 }
11418                 map<double, int> sortedDom; // sort domains by angle
11419                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11420                   sortedDom[ia->second] = ia->first;
11421                 vector<int> vnodes;
11422                 vector<int> vdom;
11423                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11424                 {
11425                   vdom.push_back(ib->second);
11426                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11427                 }
11428                 for (int ino = 0; ino < nbNodes; ino++)
11429                   vnodes.push_back(nodes[ino]);
11430                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11431               }
11432             }
11433           }
11434         }
11435       }
11436     }
11437   }
11438
11439   // --- iterate on shared faces (volumes to modify, face to extrude)
11440   //     get node id's of the face (id SMDS = id VTK)
11441   //     create flat element with old and new nodes if requested
11442
11443   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11444   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11445
11446   std::map<int, std::map<long,int> > nodeQuadDomains;
11447   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11448
11449   //MESSAGE(".. Creation of elements: simple junction");
11450   if (createJointElems)
11451   {
11452     string joints2DName = "joints2D";
11453     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11454     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11455     string joints3DName = "joints3D";
11456     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11457     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11458
11459     itface = faceDomains.begin();
11460     for (; itface != faceDomains.end(); ++itface)
11461     {
11462       DownIdType face = itface->first;
11463       std::set<int> oldNodes;
11464       std::set<int>::iterator itn;
11465       oldNodes.clear();
11466       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11467
11468       std::map<int, int> domvol = itface->second;
11469       std::map<int, int>::iterator itdom = domvol.begin();
11470       int dom1 = itdom->first;
11471       int vtkVolId = itdom->second;
11472       itdom++;
11473       int dom2 = itdom->first;
11474       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11475                                                        nodeQuadDomains);
11476       stringstream grpname;
11477       grpname << "j_";
11478       if (dom1 < dom2)
11479         grpname << dom1 << "_" << dom2;
11480       else
11481         grpname << dom2 << "_" << dom1;
11482       string namegrp = grpname.str();
11483       if (!mapOfJunctionGroups.count(namegrp))
11484         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11485       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11486       if (sgrp)
11487         sgrp->Add(vol->GetID());
11488       if (vol->GetType() == SMDSAbs_Volume)
11489         joints3DGrp->Add(vol->GetID());
11490       else if (vol->GetType() == SMDSAbs_Face)
11491         joints2DGrp->Add(vol->GetID());
11492     }
11493   }
11494
11495   // --- create volumes on multiple domain intersection if requested
11496   //     iterate on mutipleNodesToFace
11497   //     iterate on edgesMultiDomains
11498
11499   //MESSAGE(".. Creation of elements: multiple junction");
11500   if (createJointElems)
11501   {
11502     // --- iterate on mutipleNodesToFace
11503
11504     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11505     for (; itn != mutipleNodesToFace.end(); ++itn)
11506     {
11507       int node = itn->first;
11508       vector<int> orderDom = itn->second;
11509       vector<vtkIdType> orderedNodes;
11510       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11511         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11512       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11513
11514       stringstream grpname;
11515       grpname << "m2j_";
11516       grpname << 0 << "_" << 0;
11517       string namegrp = grpname.str();
11518       if (!mapOfJunctionGroups.count(namegrp))
11519         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11520       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11521       if (sgrp)
11522         sgrp->Add(face->GetID());
11523     }
11524
11525     // --- iterate on edgesMultiDomains
11526
11527     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11528     for (; ite != edgesMultiDomains.end(); ++ite)
11529     {
11530       vector<int> nodes = ite->first;
11531       vector<int> orderDom = ite->second;
11532       vector<vtkIdType> orderedNodes;
11533       if (nodes.size() == 2)
11534       {
11535         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11536         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11537           if ( orderDom.size() == 3 )
11538             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11539               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11540           else
11541             for (int idom = orderDom.size()-1; idom >=0; idom--)
11542               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11543         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11544
11545         string namegrp = "jointsMultiples";
11546         if (!mapOfJunctionGroups.count(namegrp))
11547           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11548         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11549         if (sgrp)
11550           sgrp->Add(vol->GetID());
11551       }
11552       else
11553       {
11554         //INFOS("Quadratic multiple joints not implemented");
11555         // TODO quadratic nodes
11556       }
11557     }
11558   }
11559
11560   // --- list the explicit faces and edges of the mesh that need to be modified,
11561   //     i.e. faces and edges built with one or more duplicated nodes.
11562   //     associate these faces or edges to their corresponding domain.
11563   //     only the first domain found is kept when a face or edge is shared
11564
11565   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11566   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11567   faceOrEdgeDom.clear();
11568   feDom.clear();
11569
11570   //MESSAGE(".. Modification of elements");
11571   for (int idomain = idom0; idomain < nbDomains; idomain++)
11572   {
11573     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11574     for (; itnod != nodeDomains.end(); ++itnod)
11575     {
11576       int oldId = itnod->first;
11577       //MESSAGE("     node " << oldId);
11578       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11579       for (int i = 0; i < l.ncells; i++)
11580       {
11581         int vtkId = l.cells[i];
11582         int vtkType = grid->GetCellType(vtkId);
11583         int downId = grid->CellIdToDownId(vtkId);
11584         if (downId < 0)
11585           continue; // new cells: not to be modified
11586         DownIdType aCell(downId, vtkType);
11587         int volParents[1000];
11588         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11589         for (int j = 0; j < nbvol; j++)
11590           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11591             if (!feDom.count(vtkId))
11592             {
11593               feDom[vtkId] = idomain;
11594               faceOrEdgeDom[aCell] = emptyMap;
11595               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11596               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11597               //        << " type " << vtkType << " downId " << downId);
11598             }
11599       }
11600     }
11601   }
11602
11603   // --- iterate on shared faces (volumes to modify, face to extrude)
11604   //     get node id's of the face
11605   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11606
11607   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11608   for (int m=0; m<3; m++)
11609   {
11610     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11611     itface = (*amap).begin();
11612     for (; itface != (*amap).end(); ++itface)
11613     {
11614       DownIdType face = itface->first;
11615       std::set<int> oldNodes;
11616       std::set<int>::iterator itn;
11617       oldNodes.clear();
11618       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11619       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11620       std::map<int, int> localClonedNodeIds;
11621
11622       std::map<int, int> domvol = itface->second;
11623       std::map<int, int>::iterator itdom = domvol.begin();
11624       for (; itdom != domvol.end(); ++itdom)
11625       {
11626         int idom = itdom->first;
11627         int vtkVolId = itdom->second;
11628         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11629         localClonedNodeIds.clear();
11630         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11631         {
11632           int oldId = *itn;
11633           if (nodeDomains[oldId].count(idom))
11634           {
11635             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11636             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11637           }
11638         }
11639         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11640       }
11641     }
11642   }
11643
11644   // Remove empty groups (issue 0022812)
11645   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11646   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11647   {
11648     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11649       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11650   }
11651
11652   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11653   grid->DeleteLinks();
11654
11655   CHRONOSTOP(50);
11656   counters::stats();
11657   return true;
11658 }
11659
11660 /*!
11661  * \brief Double nodes on some external faces and create flat elements.
11662  * Flat elements are mainly used by some types of mechanic calculations.
11663  *
11664  * Each group of the list must be constituted of faces.
11665  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11666  * @param theElems - list of groups of faces, where a group of faces is a set of
11667  * SMDS_MeshElements sorted by Id.
11668  * @return TRUE if operation has been completed successfully, FALSE otherwise
11669  */
11670 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11671 {
11672   // MESSAGE("-------------------------------------------------");
11673   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11674   // MESSAGE("-------------------------------------------------");
11675
11676   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11677
11678   // --- For each group of faces
11679   //     duplicate the nodes, create a flat element based on the face
11680   //     replace the nodes of the faces by their clones
11681
11682   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11683   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11684   clonedNodes.clear();
11685   intermediateNodes.clear();
11686   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11687   mapOfJunctionGroups.clear();
11688
11689   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11690   {
11691     const TIDSortedElemSet&           domain = theElems[idom];
11692     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11693     for ( ; elemItr != domain.end(); ++elemItr )
11694     {
11695       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11696       if (!aFace)
11697         continue;
11698       // MESSAGE("aFace=" << aFace->GetID());
11699       bool isQuad = aFace->IsQuadratic();
11700       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11701
11702       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11703
11704       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11705       while (nodeIt->more())
11706       {
11707         const SMDS_MeshNode* node = nodeIt->next();
11708         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11709         if (isMedium)
11710           ln2.push_back(node);
11711         else
11712           ln0.push_back(node);
11713
11714         const SMDS_MeshNode* clone = 0;
11715         if (!clonedNodes.count(node))
11716         {
11717           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11718           copyPosition( node, clone );
11719           clonedNodes[node] = clone;
11720         }
11721         else
11722           clone = clonedNodes[node];
11723
11724         if (isMedium)
11725           ln3.push_back(clone);
11726         else
11727           ln1.push_back(clone);
11728
11729         const SMDS_MeshNode* inter = 0;
11730         if (isQuad && (!isMedium))
11731         {
11732           if (!intermediateNodes.count(node))
11733           {
11734             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11735             copyPosition( node, inter );
11736             intermediateNodes[node] = inter;
11737           }
11738           else
11739             inter = intermediateNodes[node];
11740           ln4.push_back(inter);
11741         }
11742       }
11743
11744       // --- extrude the face
11745
11746       vector<const SMDS_MeshNode*> ln;
11747       SMDS_MeshVolume* vol = 0;
11748       vtkIdType aType = aFace->GetVtkType();
11749       switch (aType)
11750       {
11751       case VTK_TRIANGLE:
11752         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11753         // MESSAGE("vol prism " << vol->GetID());
11754         ln.push_back(ln1[0]);
11755         ln.push_back(ln1[1]);
11756         ln.push_back(ln1[2]);
11757         break;
11758       case VTK_QUAD:
11759         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11760         // MESSAGE("vol hexa " << vol->GetID());
11761         ln.push_back(ln1[0]);
11762         ln.push_back(ln1[1]);
11763         ln.push_back(ln1[2]);
11764         ln.push_back(ln1[3]);
11765         break;
11766       case VTK_QUADRATIC_TRIANGLE:
11767         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11768                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11769         // MESSAGE("vol quad prism " << vol->GetID());
11770         ln.push_back(ln1[0]);
11771         ln.push_back(ln1[1]);
11772         ln.push_back(ln1[2]);
11773         ln.push_back(ln3[0]);
11774         ln.push_back(ln3[1]);
11775         ln.push_back(ln3[2]);
11776         break;
11777       case VTK_QUADRATIC_QUAD:
11778         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11779         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11780         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11781         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11782                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11783                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11784         // MESSAGE("vol quad hexa " << vol->GetID());
11785         ln.push_back(ln1[0]);
11786         ln.push_back(ln1[1]);
11787         ln.push_back(ln1[2]);
11788         ln.push_back(ln1[3]);
11789         ln.push_back(ln3[0]);
11790         ln.push_back(ln3[1]);
11791         ln.push_back(ln3[2]);
11792         ln.push_back(ln3[3]);
11793         break;
11794       case VTK_POLYGON:
11795         break;
11796       default:
11797         break;
11798       }
11799
11800       if (vol)
11801       {
11802         stringstream grpname;
11803         grpname << "jf_";
11804         grpname << idom;
11805         string namegrp = grpname.str();
11806         if (!mapOfJunctionGroups.count(namegrp))
11807           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11808         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11809         if (sgrp)
11810           sgrp->Add(vol->GetID());
11811       }
11812
11813       // --- modify the face
11814
11815       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11816     }
11817   }
11818   return true;
11819 }
11820
11821 /*!
11822  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11823  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11824  *  groups of faces to remove inside the object, (idem edges).
11825  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11826  */
11827 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11828                                       const TopoDS_Shape&             theShape,
11829                                       SMESH_NodeSearcher*             theNodeSearcher,
11830                                       const char*                     groupName,
11831                                       std::vector<double>&            nodesCoords,
11832                                       std::vector<std::vector<int> >& listOfListOfNodes)
11833 {
11834   // MESSAGE("--------------------------------");
11835   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11836   // MESSAGE("--------------------------------");
11837
11838   // --- zone of volumes to remove is given :
11839   //     1 either by a geom shape (one or more vertices) and a radius,
11840   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11841   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11842   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11843   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11844   //     defined by it's name.
11845
11846   SMESHDS_GroupBase* groupDS = 0;
11847   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11848   while ( groupIt->more() )
11849   {
11850     groupDS = 0;
11851     SMESH_Group * group = groupIt->next();
11852     if ( !group ) continue;
11853     groupDS = group->GetGroupDS();
11854     if ( !groupDS || groupDS->IsEmpty() ) continue;
11855     std::string grpName = group->GetName();
11856     //MESSAGE("grpName=" << grpName);
11857     if (grpName == groupName)
11858       break;
11859     else
11860       groupDS = 0;
11861   }
11862
11863   bool isNodeGroup = false;
11864   bool isNodeCoords = false;
11865   if (groupDS)
11866   {
11867     if (groupDS->GetType() != SMDSAbs_Node)
11868       return;
11869     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11870   }
11871
11872   if (nodesCoords.size() > 0)
11873     isNodeCoords = true; // a list o nodes given by their coordinates
11874   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11875
11876   // --- define groups to build
11877
11878   // --- group of SMDS volumes
11879   string grpvName = groupName;
11880   grpvName += "_vol";
11881   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11882   if (!grp)
11883   {
11884     MESSAGE("group not created " << grpvName);
11885     return;
11886   }
11887   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11888
11889   // --- group of SMDS faces on the skin
11890   string grpsName = groupName;
11891   grpsName += "_skin";
11892   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11893   if (!grps)
11894   {
11895     MESSAGE("group not created " << grpsName);
11896     return;
11897   }
11898   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11899
11900   // --- group of SMDS faces internal (several shapes)
11901   string grpiName = groupName;
11902   grpiName += "_internalFaces";
11903   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11904   if (!grpi)
11905   {
11906     MESSAGE("group not created " << grpiName);
11907     return;
11908   }
11909   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11910
11911   // --- group of SMDS faces internal (several shapes)
11912   string grpeiName = groupName;
11913   grpeiName += "_internalEdges";
11914   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11915   if (!grpei)
11916   {
11917     MESSAGE("group not created " << grpeiName);
11918     return;
11919   }
11920   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11921
11922   // --- build downward connectivity
11923
11924   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11925   meshDS->BuildDownWardConnectivity(true);
11926   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11927
11928   // --- set of volumes detected inside
11929
11930   std::set<int> setOfInsideVol;
11931   std::set<int> setOfVolToCheck;
11932
11933   std::vector<gp_Pnt> gpnts;
11934   gpnts.clear();
11935
11936   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11937   {
11938     //MESSAGE("group of nodes provided");
11939     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11940     while ( elemIt->more() )
11941     {
11942       const SMDS_MeshElement* elem = elemIt->next();
11943       if (!elem)
11944         continue;
11945       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11946       if (!node)
11947         continue;
11948       SMDS_MeshElement* vol = 0;
11949       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11950       while (volItr->more())
11951       {
11952         vol = (SMDS_MeshElement*)volItr->next();
11953         setOfInsideVol.insert(vol->GetVtkID());
11954         sgrp->Add(vol->GetID());
11955       }
11956     }
11957   }
11958   else if (isNodeCoords)
11959   {
11960     //MESSAGE("list of nodes coordinates provided");
11961     size_t i = 0;
11962     int k = 0;
11963     while ( i < nodesCoords.size()-2 )
11964     {
11965       double x = nodesCoords[i++];
11966       double y = nodesCoords[i++];
11967       double z = nodesCoords[i++];
11968       gp_Pnt p = gp_Pnt(x, y ,z);
11969       gpnts.push_back(p);
11970       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11971       k++;
11972     }
11973   }
11974   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11975   {
11976     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11977     TopTools_IndexedMapOfShape vertexMap;
11978     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11979     gp_Pnt p = gp_Pnt(0,0,0);
11980     if (vertexMap.Extent() < 1)
11981       return;
11982
11983     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11984     {
11985       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11986       p = BRep_Tool::Pnt(vertex);
11987       gpnts.push_back(p);
11988       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11989     }
11990   }
11991
11992   if (gpnts.size() > 0)
11993   {
11994     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11995     //MESSAGE("startNode->nodeId " << nodeId);
11996
11997     double radius2 = radius*radius;
11998     //MESSAGE("radius2 " << radius2);
11999
12000     // --- volumes on start node
12001
12002     setOfVolToCheck.clear();
12003     SMDS_MeshElement* startVol = 0;
12004     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12005     while (volItr->more())
12006     {
12007       startVol = (SMDS_MeshElement*)volItr->next();
12008       setOfVolToCheck.insert(startVol->GetVtkID());
12009     }
12010     if (setOfVolToCheck.empty())
12011     {
12012       MESSAGE("No volumes found");
12013       return;
12014     }
12015
12016     // --- starting with central volumes then their neighbors, check if they are inside
12017     //     or outside the domain, until no more new neighbor volume is inside.
12018     //     Fill the group of inside volumes
12019
12020     std::map<int, double> mapOfNodeDistance2;
12021     mapOfNodeDistance2.clear();
12022     std::set<int> setOfOutsideVol;
12023     while (!setOfVolToCheck.empty())
12024     {
12025       std::set<int>::iterator it = setOfVolToCheck.begin();
12026       int vtkId = *it;
12027       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12028       bool volInside = false;
12029       vtkIdType npts = 0;
12030       vtkIdType const *pts(nullptr);
12031       grid->GetCellPoints(vtkId, npts, pts);
12032       for (int i=0; i<npts; i++)
12033       {
12034         double distance2 = 0;
12035         if (mapOfNodeDistance2.count(pts[i]))
12036         {
12037           distance2 = mapOfNodeDistance2[pts[i]];
12038           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12039         }
12040         else
12041         {
12042           double *coords = grid->GetPoint(pts[i]);
12043           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12044           distance2 = 1.E40;
12045           for ( size_t j = 0; j < gpnts.size(); j++ )
12046           {
12047             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12048             if (d2 < distance2)
12049             {
12050               distance2 = d2;
12051               if (distance2 < radius2)
12052                 break;
12053             }
12054           }
12055           mapOfNodeDistance2[pts[i]] = distance2;
12056           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12057         }
12058         if (distance2 < radius2)
12059         {
12060           volInside = true; // one or more nodes inside the domain
12061           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12062           break;
12063         }
12064       }
12065       if (volInside)
12066       {
12067         setOfInsideVol.insert(vtkId);
12068         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12069         int neighborsVtkIds[NBMAXNEIGHBORS];
12070         int downIds[NBMAXNEIGHBORS];
12071         unsigned char downTypes[NBMAXNEIGHBORS];
12072         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12073         for (int n = 0; n < nbNeighbors; n++)
12074           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12075             setOfVolToCheck.insert(neighborsVtkIds[n]);
12076       }
12077       else
12078       {
12079         setOfOutsideVol.insert(vtkId);
12080         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12081       }
12082       setOfVolToCheck.erase(vtkId);
12083     }
12084   }
12085
12086   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12087   //     If yes, add the volume to the inside set
12088
12089   bool addedInside = true;
12090   std::set<int> setOfVolToReCheck;
12091   while (addedInside)
12092   {
12093     //MESSAGE(" --------------------------- re check");
12094     addedInside = false;
12095     std::set<int>::iterator itv = setOfInsideVol.begin();
12096     for (; itv != setOfInsideVol.end(); ++itv)
12097     {
12098       int vtkId = *itv;
12099       int neighborsVtkIds[NBMAXNEIGHBORS];
12100       int downIds[NBMAXNEIGHBORS];
12101       unsigned char downTypes[NBMAXNEIGHBORS];
12102       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12103       for (int n = 0; n < nbNeighbors; n++)
12104         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12105           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12106     }
12107     setOfVolToCheck = setOfVolToReCheck;
12108     setOfVolToReCheck.clear();
12109     while  (!setOfVolToCheck.empty())
12110     {
12111       std::set<int>::iterator it = setOfVolToCheck.begin();
12112       int vtkId = *it;
12113       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12114       {
12115         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12116         int countInside = 0;
12117         int neighborsVtkIds[NBMAXNEIGHBORS];
12118         int downIds[NBMAXNEIGHBORS];
12119         unsigned char downTypes[NBMAXNEIGHBORS];
12120         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12121         for (int n = 0; n < nbNeighbors; n++)
12122           if (setOfInsideVol.count(neighborsVtkIds[n]))
12123             countInside++;
12124         //MESSAGE("countInside " << countInside);
12125         if (countInside > 1)
12126         {
12127           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12128           setOfInsideVol.insert(vtkId);
12129           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12130           addedInside = true;
12131         }
12132         else
12133           setOfVolToReCheck.insert(vtkId);
12134       }
12135       setOfVolToCheck.erase(vtkId);
12136     }
12137   }
12138
12139   // --- map of Downward faces at the boundary, inside the global volume
12140   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12141   //     fill group of SMDS faces inside the volume (when several volume shapes)
12142   //     fill group of SMDS faces on the skin of the global volume (if skin)
12143
12144   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12145   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12146   std::set<int>::iterator it = setOfInsideVol.begin();
12147   for (; it != setOfInsideVol.end(); ++it)
12148   {
12149     int vtkId = *it;
12150     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12151     int neighborsVtkIds[NBMAXNEIGHBORS];
12152     int downIds[NBMAXNEIGHBORS];
12153     unsigned char downTypes[NBMAXNEIGHBORS];
12154     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12155     for (int n = 0; n < nbNeighbors; n++)
12156     {
12157       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12158       if (neighborDim == 3)
12159       {
12160         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12161         {
12162           DownIdType face(downIds[n], downTypes[n]);
12163           boundaryFaces[face] = vtkId;
12164         }
12165         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12166         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12167         if (vtkFaceId >= 0)
12168         {
12169           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12170           // find also the smds edges on this face
12171           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12172           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12173           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12174           for (int i = 0; i < nbEdges; i++)
12175           {
12176             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12177             if (vtkEdgeId >= 0)
12178               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12179           }
12180         }
12181       }
12182       else if (neighborDim == 2) // skin of the volume
12183       {
12184         DownIdType face(downIds[n], downTypes[n]);
12185         skinFaces[face] = vtkId;
12186         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12187         if (vtkFaceId >= 0)
12188           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12189       }
12190     }
12191   }
12192
12193   // --- identify the edges constituting the wire of each subshape on the skin
12194   //     define polylines with the nodes of edges, equivalent to wires
12195   //     project polylines on subshapes, and partition, to get geom faces
12196
12197   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12198   std::set<int> emptySet;
12199   emptySet.clear();
12200   std::set<int> shapeIds;
12201
12202   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12203   while (itelem->more())
12204   {
12205     const SMDS_MeshElement *elem = itelem->next();
12206     int shapeId = elem->getshapeId();
12207     int   vtkId = elem->GetVtkID();
12208     if (!shapeIdToVtkIdSet.count(shapeId))
12209     {
12210       shapeIdToVtkIdSet[shapeId] = emptySet;
12211       shapeIds.insert(shapeId);
12212     }
12213     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12214   }
12215
12216   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12217   std::set<DownIdType, DownIdCompare> emptyEdges;
12218   emptyEdges.clear();
12219
12220   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12221   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12222   {
12223     int shapeId = itShape->first;
12224     //MESSAGE(" --- Shape ID --- "<< shapeId);
12225     shapeIdToEdges[shapeId] = emptyEdges;
12226
12227     std::vector<int> nodesEdges;
12228
12229     std::set<int>::iterator its = itShape->second.begin();
12230     for (; its != itShape->second.end(); ++its)
12231     {
12232       int vtkId = *its;
12233       //MESSAGE("     " << vtkId);
12234       int neighborsVtkIds[NBMAXNEIGHBORS];
12235       int downIds[NBMAXNEIGHBORS];
12236       unsigned char downTypes[NBMAXNEIGHBORS];
12237       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12238       for (int n = 0; n < nbNeighbors; n++)
12239       {
12240         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12241           continue;
12242         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12243         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12244         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12245         {
12246           DownIdType edge(downIds[n], downTypes[n]);
12247           if (!shapeIdToEdges[shapeId].count(edge))
12248           {
12249             shapeIdToEdges[shapeId].insert(edge);
12250             int vtkNodeId[3];
12251             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12252             nodesEdges.push_back(vtkNodeId[0]);
12253             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12254             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12255           }
12256         }
12257       }
12258     }
12259
12260     std::list<int> order;
12261     order.clear();
12262     if (nodesEdges.size() > 0)
12263     {
12264       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12265       nodesEdges[0] = -1;
12266       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12267       nodesEdges[1] = -1; // do not reuse this edge
12268       bool found = true;
12269       while (found)
12270       {
12271         int nodeTofind = order.back(); // try first to push back
12272         int i = 0;
12273         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12274           if (nodesEdges[i] == nodeTofind)
12275             break;
12276         if ( i == (int) nodesEdges.size() )
12277           found = false; // no follower found on back
12278         else
12279         {
12280           if (i%2) // odd ==> use the previous one
12281             if (nodesEdges[i-1] < 0)
12282               found = false;
12283             else
12284             {
12285               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12286               nodesEdges[i-1] = -1;
12287             }
12288           else // even ==> use the next one
12289             if (nodesEdges[i+1] < 0)
12290               found = false;
12291             else
12292             {
12293               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12294               nodesEdges[i+1] = -1;
12295             }
12296         }
12297         if (found)
12298           continue;
12299         // try to push front
12300         found = true;
12301         nodeTofind = order.front(); // try to push front
12302         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12303           if ( nodesEdges[i] == nodeTofind )
12304             break;
12305         if ( i == (int)nodesEdges.size() )
12306         {
12307           found = false; // no predecessor found on front
12308           continue;
12309         }
12310         if (i%2) // odd ==> use the previous one
12311           if (nodesEdges[i-1] < 0)
12312             found = false;
12313           else
12314           {
12315             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12316             nodesEdges[i-1] = -1;
12317           }
12318         else // even ==> use the next one
12319           if (nodesEdges[i+1] < 0)
12320             found = false;
12321           else
12322           {
12323             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12324             nodesEdges[i+1] = -1;
12325           }
12326       }
12327     }
12328
12329
12330     std::vector<int> nodes;
12331     nodes.push_back(shapeId);
12332     std::list<int>::iterator itl = order.begin();
12333     for (; itl != order.end(); itl++)
12334     {
12335       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12336       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12337     }
12338     listOfListOfNodes.push_back(nodes);
12339   }
12340
12341   //     partition geom faces with blocFissure
12342   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12343   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12344
12345   return;
12346 }
12347
12348
12349 //================================================================================
12350 /*!
12351  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12352  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12353  * \return TRUE if operation has been completed successfully, FALSE otherwise
12354  */
12355 //================================================================================
12356
12357 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12358 {
12359   // iterates on volume elements and detect all free faces on them
12360   SMESHDS_Mesh* aMesh = GetMeshDS();
12361   if (!aMesh)
12362     return false;
12363
12364   ElemFeatures faceType( SMDSAbs_Face );
12365   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12366   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12367   while(vIt->more())
12368   {
12369     const SMDS_MeshVolume* volume = vIt->next();
12370     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12371     vTool.SetExternalNormal();
12372     const int iQuad = volume->IsQuadratic();
12373     faceType.SetQuad( iQuad );
12374     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12375     {
12376       if (!vTool.IsFreeFace(iface))
12377         continue;
12378       nbFree++;
12379       vector<const SMDS_MeshNode *> nodes;
12380       int nbFaceNodes = vTool.NbFaceNodes(iface);
12381       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12382       int inode = 0;
12383       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12384         nodes.push_back(faceNodes[inode]);
12385
12386       if (iQuad) // add medium nodes
12387       {
12388         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12389           nodes.push_back(faceNodes[inode]);
12390         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12391           nodes.push_back(faceNodes[8]);
12392       }
12393       // add new face based on volume nodes
12394       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12395       {
12396         nbExisted++; // face already exists
12397       }
12398       else
12399       {
12400         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12401         nbCreated++;
12402       }
12403     }
12404   }
12405   return ( nbFree == ( nbExisted + nbCreated ));
12406 }
12407
12408 namespace
12409 {
12410   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12411   {
12412     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12413       return n;
12414     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12415   }
12416 }
12417 //================================================================================
12418 /*!
12419  * \brief Creates missing boundary elements
12420  *  \param elements - elements whose boundary is to be checked
12421  *  \param dimension - defines type of boundary elements to create
12422  *  \param group - a group to store created boundary elements in
12423  *  \param targetMesh - a mesh to store created boundary elements in
12424  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12425  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12426  *                                boundary elements will be copied into the targetMesh
12427  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12428  *                                boundary elements will be added into the new group
12429  *  \param aroundElements - if true, elements will be created on boundary of given
12430  *                          elements else, on boundary of the whole mesh.
12431  * \return nb of added boundary elements
12432  */
12433 //================================================================================
12434
12435 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12436                                        Bnd_Dimension           dimension,
12437                                        SMESH_Group*            group/*=0*/,
12438                                        SMESH_Mesh*             targetMesh/*=0*/,
12439                                        bool                    toCopyElements/*=false*/,
12440                                        bool                    toCopyExistingBoundary/*=false*/,
12441                                        bool                    toAddExistingBondary/*= false*/,
12442                                        bool                    aroundElements/*= false*/)
12443 {
12444   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12445   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12446   // hope that all elements are of the same type, do not check them all
12447   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12448     throw SALOME_Exception(LOCALIZED("wrong element type"));
12449
12450   if ( !targetMesh )
12451     toCopyElements = toCopyExistingBoundary = false;
12452
12453   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12454   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12455   int nbAddedBnd = 0;
12456
12457   // editor adding present bnd elements and optionally holding elements to add to the group
12458   SMESH_MeshEditor* presentEditor;
12459   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12460   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12461
12462   SMESH_MesherHelper helper( *myMesh );
12463   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12464   SMDS_VolumeTool vTool;
12465   TIDSortedElemSet avoidSet;
12466   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12467   size_t inode;
12468
12469   typedef vector<const SMDS_MeshNode*> TConnectivity;
12470   TConnectivity tgtNodes;
12471   ElemFeatures elemKind( missType ), elemToCopy;
12472
12473   vector<const SMDS_MeshElement*> presentBndElems;
12474   vector<TConnectivity>           missingBndElems;
12475   vector<int>                     freeFacets;
12476   TConnectivity nodes, elemNodes;
12477
12478   SMDS_ElemIteratorPtr eIt;
12479   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12480   else                  eIt = SMESHUtils::elemSetIterator( elements );
12481
12482   while ( eIt->more() )
12483   {
12484     const SMDS_MeshElement* elem = eIt->next();
12485     const int              iQuad = elem->IsQuadratic();
12486     elemKind.SetQuad( iQuad );
12487
12488     // ------------------------------------------------------------------------------------
12489     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12490     // ------------------------------------------------------------------------------------
12491     presentBndElems.clear();
12492     missingBndElems.clear();
12493     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12494     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12495     {
12496       const SMDS_MeshElement* otherVol = 0;
12497       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12498       {
12499         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12500              ( !aroundElements || elements.count( otherVol )))
12501           continue;
12502         freeFacets.push_back( iface );
12503       }
12504       if ( missType == SMDSAbs_Face )
12505         vTool.SetExternalNormal();
12506       for ( size_t i = 0; i < freeFacets.size(); ++i )
12507       {
12508         int                iface = freeFacets[i];
12509         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12510         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12511         if ( missType == SMDSAbs_Edge ) // boundary edges
12512         {
12513           nodes.resize( 2+iQuad );
12514           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12515           {
12516             for ( size_t j = 0; j < nodes.size(); ++j )
12517               nodes[ j ] = nn[ i+j ];
12518             if ( const SMDS_MeshElement* edge =
12519                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12520               presentBndElems.push_back( edge );
12521             else
12522               missingBndElems.push_back( nodes );
12523           }
12524         }
12525         else // boundary face
12526         {
12527           nodes.clear();
12528           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12529             nodes.push_back( nn[inode] ); // add corner nodes
12530           if (iQuad)
12531             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12532               nodes.push_back( nn[inode] ); // add medium nodes
12533           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12534           if ( iCenter > 0 )
12535             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12536
12537           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12538                                                                SMDSAbs_Face, /*noMedium=*/false ))
12539             presentBndElems.push_back( f );
12540           else
12541             missingBndElems.push_back( nodes );
12542
12543           if ( targetMesh != myMesh )
12544           {
12545             // add 1D elements on face boundary to be added to a new mesh
12546             const SMDS_MeshElement* edge;
12547             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12548             {
12549               if ( iQuad )
12550                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12551               else
12552                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12553               if ( edge && avoidSet.insert( edge ).second )
12554                 presentBndElems.push_back( edge );
12555             }
12556           }
12557         }
12558       }
12559     }
12560     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12561     {
12562       avoidSet.clear(), avoidSet.insert( elem );
12563       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12564                         SMDS_MeshElement::iterator() );
12565       elemNodes.push_back( elemNodes[0] );
12566       nodes.resize( 2 + iQuad );
12567       const int nbLinks = elem->NbCornerNodes();
12568       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12569       {
12570         nodes[0] = elemNodes[iN];
12571         nodes[1] = elemNodes[iN+1+iQuad];
12572         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12573           continue; // not free link
12574
12575         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12576         if ( const SMDS_MeshElement* edge =
12577              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12578           presentBndElems.push_back( edge );
12579         else
12580           missingBndElems.push_back( nodes );
12581       }
12582     }
12583
12584     // ---------------------------------
12585     // 2. Add missing boundary elements
12586     // ---------------------------------
12587     if ( targetMesh != myMesh )
12588       // instead of making a map of nodes in this mesh and targetMesh,
12589       // we create nodes with same IDs.
12590       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12591       {
12592         TConnectivity& srcNodes = missingBndElems[i];
12593         tgtNodes.resize( srcNodes.size() );
12594         for ( inode = 0; inode < srcNodes.size(); ++inode )
12595           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12596         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12597                                                                        missType,
12598                                                                        /*noMedium=*/false))
12599           continue;
12600         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12601         ++nbAddedBnd;
12602       }
12603     else
12604       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12605       {
12606         TConnectivity& nodes = missingBndElems[ i ];
12607         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12608                                                                        missType,
12609                                                                        /*noMedium=*/false))
12610           continue;
12611         SMDS_MeshElement* newElem =
12612           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12613         nbAddedBnd += bool( newElem );
12614
12615         // try to set a new element to a shape
12616         if ( myMesh->HasShapeToMesh() )
12617         {
12618           bool ok = true;
12619           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12620           const size_t nbN = nodes.size() / (iQuad+1 );
12621           for ( inode = 0; inode < nbN && ok; ++inode )
12622           {
12623             pair<int, TopAbs_ShapeEnum> i_stype =
12624               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12625             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12626               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12627           }
12628           if ( ok && mediumShapes.size() > 1 )
12629           {
12630             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12631             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12632             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12633             {
12634               if (( ok = ( stype_i->first != stype_i_0.first )))
12635                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12636                                         aMesh->IndexToShape( stype_i_0.second ));
12637             }
12638           }
12639           if ( ok && mediumShapes.begin()->first == missShapeType )
12640             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12641         }
12642       }
12643
12644     // ----------------------------------
12645     // 3. Copy present boundary elements
12646     // ----------------------------------
12647     if ( toCopyExistingBoundary )
12648       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12649       {
12650         const SMDS_MeshElement* e = presentBndElems[i];
12651         tgtNodes.resize( e->NbNodes() );
12652         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12653           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12654         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12655       }
12656     else // store present elements to add them to a group
12657       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12658       {
12659         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12660       }
12661
12662   } // loop on given elements
12663
12664   // ---------------------------------------------
12665   // 4. Fill group with boundary elements
12666   // ---------------------------------------------
12667   if ( group )
12668   {
12669     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12670       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12671         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12672   }
12673   tgtEditor.myLastCreatedElems.clear();
12674   tgtEditor2.myLastCreatedElems.clear();
12675
12676   // -----------------------
12677   // 5. Copy given elements
12678   // -----------------------
12679   if ( toCopyElements && targetMesh != myMesh )
12680   {
12681     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12682     else                  eIt = SMESHUtils::elemSetIterator( elements );
12683     while (eIt->more())
12684     {
12685       const SMDS_MeshElement* elem = eIt->next();
12686       tgtNodes.resize( elem->NbNodes() );
12687       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12688         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12689       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12690
12691       tgtEditor.myLastCreatedElems.clear();
12692     }
12693   }
12694   return nbAddedBnd;
12695 }
12696
12697 //================================================================================
12698 /*!
12699  * \brief Copy node position and set \a to node on the same geometry
12700  */
12701 //================================================================================
12702
12703 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12704                                      const SMDS_MeshNode* to )
12705 {
12706   if ( !from || !to ) return;
12707
12708   SMDS_PositionPtr pos = from->GetPosition();
12709   if ( !pos || from->getshapeId() < 1 ) return;
12710
12711   switch ( pos->GetTypeOfPosition() )
12712   {
12713   case SMDS_TOP_3DSPACE: break;
12714
12715   case SMDS_TOP_FACE:
12716   {
12717     SMDS_FacePositionPtr fPos = pos;
12718     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12719                                 fPos->GetUParameter(), fPos->GetVParameter() );
12720     break;
12721   }
12722   case SMDS_TOP_EDGE:
12723   {
12724     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12725     SMDS_EdgePositionPtr ePos = pos;
12726     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12727     break;
12728   }
12729   case SMDS_TOP_VERTEX:
12730   {
12731     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12732     break;
12733   }
12734   case SMDS_TOP_UNSPEC:
12735   default:;
12736   }
12737 }