Salome HOME
ff121002f5de4d992663f11670586a9ae1ab1a95
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #include <smIdType.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 //=======================================================================
111 //function : SMESH_MeshEditor
112 //purpose  :
113 //=======================================================================
114
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116   :myMesh( theMesh ) // theMesh may be NULL
117 {
118 }
119
120 //================================================================================
121 /*!
122  * \brief Return mesh DS
123  */
124 //================================================================================
125
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 {
128   return myMesh->GetMeshDS();
129 }
130
131
132 //================================================================================
133 /*!
134  * \brief Clears myLastCreatedNodes and myLastCreatedElems
135  */
136 //================================================================================
137
138 void SMESH_MeshEditor::ClearLastCreated()
139 {
140   SMESHUtils::FreeVector( myLastCreatedElems );
141   SMESHUtils::FreeVector( myLastCreatedNodes );
142 }
143
144 //================================================================================
145 /*!
146  * \brief Initializes members by an existing element
147  *  \param [in] elem - the source element
148  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149  */
150 //================================================================================
151
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
154 {
155   if ( elem )
156   {
157     myType = elem->GetType();
158     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159     {
160       myIsPoly = elem->IsPoly();
161       if ( myIsPoly )
162       {
163         myIsQuad = elem->IsQuadratic();
164         if ( myType == SMDSAbs_Volume && !basicOnly )
165         {
166           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const smIdType ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],node[13],node[14],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12],node[13],node[14] );
300       }
301       else if (nbnode == 18) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],
306                                                  node[15],node[16],node[17],ID );
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],
311                                                  node[15],node[16],node[17] );
312       }
313       else if (nbnode == 20) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19] );
324       }
325       else if (nbnode == 27) {
326         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327                                                  node[4], node[5], node[6], node[7],
328                                                  node[8], node[9], node[10],node[11],
329                                                  node[12],node[13],node[14],node[15],
330                                                  node[16],node[17],node[18],node[19],
331                                                  node[20],node[21],node[22],node[23],
332                                                  node[24],node[25],node[26], ID);
333         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
334                                                  node[4], node[5], node[6], node[7],
335                                                  node[8], node[9], node[10],node[11],
336                                                  node[12],node[13],node[14],node[15],
337                                                  node[16],node[17],node[18],node[19],
338                                                  node[20],node[21],node[22],node[23],
339                                                  node[24],node[25],node[26] );
340       }
341     }
342     else if ( !features.myIsQuad )
343     {
344       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
346     }
347     else
348     {
349       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
351     }
352     break;
353
354   case SMDSAbs_Edge:
355     if ( nbnode == 2 ) {
356       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357       else           e = mesh->AddEdge      (node[0], node[1] );
358     }
359     else if ( nbnode == 3 ) {
360       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
362     }
363     break;
364
365   case SMDSAbs_0DElement:
366     if ( nbnode == 1 ) {
367       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368       else           e = mesh->Add0DElement      (node[0] );
369     }
370     break;
371
372   case SMDSAbs_Node:
373     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
375     break;
376
377   case SMDSAbs_Ball:
378     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
380     break;
381
382   default:;
383   }
384   if ( e ) myLastCreatedElems.push_back( e );
385   return e;
386 }
387
388 //=======================================================================
389 /*!
390  * \brief Add element
391  */
392 //=======================================================================
393
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395                                                const ElemFeatures&      features)
396 {
397   vector<const SMDS_MeshNode*> nodes;
398   nodes.reserve( nodeIDs.size() );
399   vector<smIdType>::const_iterator id = nodeIDs.begin();
400   while ( id != nodeIDs.end() ) {
401     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402       nodes.push_back( node );
403     else
404       return 0;
405   }
406   return AddElement( nodes, features );
407 }
408
409 //=======================================================================
410 //function : Remove
411 //purpose  : Remove a node or an element.
412 //           Modify a compute state of sub-meshes which become empty
413 //=======================================================================
414
415 smIdType SMESH_MeshEditor::Remove (const list< smIdType >& theIDs,
416                               const bool         isNodes )
417 {
418   ClearLastCreated();
419
420   SMESHDS_Mesh* aMesh = GetMeshDS();
421   set< SMESH_subMesh *> smmap;
422
423   smIdType removed = 0;
424   list<smIdType>::const_iterator it = theIDs.begin();
425   for ( ; it != theIDs.end(); it++ ) {
426     const SMDS_MeshElement * elem;
427     if ( isNodes )
428       elem = aMesh->FindNode( *it );
429     else
430       elem = aMesh->FindElement( *it );
431     if ( !elem )
432       continue;
433
434     // Notify VERTEX sub-meshes about modification
435     if ( isNodes ) {
436       const SMDS_MeshNode* node = cast2Node( elem );
437       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438         if ( int aShapeID = node->getshapeId() )
439           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440             smmap.insert( sm );
441     }
442     // Find sub-meshes to notify about modification
443     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444     //     while ( nodeIt->more() ) {
445     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
447     //       if ( aPosition.get() ) {
448     //         if ( int aShapeID = aPosition->GetShapeId() ) {
449     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450     //             smmap.insert( sm );
451     //         }
452     //       }
453     //     }
454
455     // Do remove
456     if ( isNodes )
457       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
458     else
459       aMesh->RemoveElement( elem );
460     removed++;
461   }
462
463   // Notify sub-meshes about modification
464   if ( !smmap.empty() ) {
465     set< SMESH_subMesh *>::iterator smIt;
466     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468   }
469
470   //   // Check if the whole mesh becomes empty
471   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
473
474   return removed;
475 }
476
477 //================================================================================
478 /*!
479  * \brief Create 0D elements on all nodes of the given object.
480  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
481  *                    the all mesh is treated
482  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483  *  \param duplicateElements - to add one more 0D element to a node or not
484  */
485 //================================================================================
486
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488                                                    TIDSortedElemSet&       all0DElems,
489                                                    const bool              duplicateElements )
490 {
491   SMDS_ElemIteratorPtr elemIt;
492   if ( elements.empty() )
493   {
494     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
495   }
496   else
497   {
498     elemIt = SMESHUtils::elemSetIterator( elements );
499   }
500
501   while ( elemIt->more() )
502   {
503     const SMDS_MeshElement* e = elemIt->next();
504     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505     while ( nodeIt->more() )
506     {
507       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509       if ( duplicateElements || !it0D->more() )
510       {
511         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
512         all0DElems.insert( myLastCreatedElems.back() );
513       }
514       while ( it0D->more() )
515         all0DElems.insert( it0D->next() );
516     }
517   }
518 }
519
520 //=======================================================================
521 //function : FindShape
522 //purpose  : Return an index of the shape theElem is on
523 //           or zero if a shape not found
524 //=======================================================================
525
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
527 {
528   ClearLastCreated();
529
530   SMESHDS_Mesh * aMesh = GetMeshDS();
531   if ( aMesh->ShapeToMesh().IsNull() )
532     return 0;
533
534   int aShapeID = theElem->getshapeId();
535   if ( aShapeID < 1 )
536     return 0;
537
538   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539     if ( sm->Contains( theElem ))
540       return aShapeID;
541
542   if ( theElem->GetType() == SMDSAbs_Node ) {
543     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544   }
545   else {
546     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547   }
548
549   TopoDS_Shape aShape; // the shape a node of theElem is on
550   if ( theElem->GetType() != SMDSAbs_Node )
551   {
552     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553     while ( nodeIt->more() ) {
554       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555       if ((aShapeID = node->getshapeId()) > 0) {
556         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557           if ( sm->Contains( theElem ))
558             return aShapeID;
559           if ( aShape.IsNull() )
560             aShape = aMesh->IndexToShape( aShapeID );
561         }
562       }
563     }
564   }
565
566   // None of nodes is on a proper shape,
567   // find the shape among ancestors of aShape on which a node is
568   if ( !aShape.IsNull() ) {
569     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570     for ( ; ancIt.More(); ancIt.Next() ) {
571       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572       if ( sm && sm->Contains( theElem ))
573         return aMesh->ShapeToIndex( ancIt.Value() );
574     }
575   }
576   else
577   {
578     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579     while ( const SMESHDS_SubMesh* sm = smIt->next() )
580       if ( sm->Contains( theElem ))
581         return sm->GetID();
582   }
583
584   return 0;
585 }
586
587 //=======================================================================
588 //function : IsMedium
589 //purpose  :
590 //=======================================================================
591
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
593                                 const SMDSAbs_ElementType typeToCheck)
594 {
595   bool isMedium = false;
596   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597   while (it->more() && !isMedium ) {
598     const SMDS_MeshElement* elem = it->next();
599     isMedium = elem->IsMediumNode(node);
600   }
601   return isMedium;
602 }
603
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose  : Shift nodes in the array corresponded to quadratic triangle
607 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
609
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
611 {
612   const SMDS_MeshNode* nd1 = aNodes[0];
613   aNodes[0] = aNodes[1];
614   aNodes[1] = aNodes[2];
615   aNodes[2] = nd1;
616   const SMDS_MeshNode* nd2 = aNodes[3];
617   aNodes[3] = aNodes[4];
618   aNodes[4] = aNodes[5];
619   aNodes[5] = nd2;
620 }
621
622 //=======================================================================
623 //function : getNodesFromTwoTria
624 //purpose  : 
625 //=======================================================================
626
627 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
628                                 const SMDS_MeshElement * theTria2,
629                                 vector< const SMDS_MeshNode*>& N1,
630                                 vector< const SMDS_MeshNode*>& N2)
631 {
632   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
633   if ( N1.size() < 6 ) return false;
634   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
635   if ( N2.size() < 6 ) return false;
636
637   int sames[3] = {-1,-1,-1};
638   int nbsames = 0;
639   int i, j;
640   for(i=0; i<3; i++) {
641     for(j=0; j<3; j++) {
642       if(N1[i]==N2[j]) {
643         sames[i] = j;
644         nbsames++;
645         break;
646       }
647     }
648   }
649   if(nbsames!=2) return false;
650   if(sames[0]>-1) {
651     shiftNodesQuadTria(N1);
652     if(sames[1]>-1) {
653       shiftNodesQuadTria(N1);
654     }
655   }
656   i = sames[0] + sames[1] + sames[2];
657   for(; i<2; i++) {
658     shiftNodesQuadTria(N2);
659   }
660   // now we receive following N1 and N2 (using numeration as in the image below)
661   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
662   // i.e. first nodes from both arrays form a new diagonal
663   return true;
664 }
665
666 //=======================================================================
667 //function : InverseDiag
668 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
669 //           but having other common link.
670 //           Return False if args are improper
671 //=======================================================================
672
673 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
674                                     const SMDS_MeshElement * theTria2 )
675 {
676   ClearLastCreated();
677
678   if ( !theTria1 || !theTria2 ||
679        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
680        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
681        theTria1->GetType() != SMDSAbs_Face ||
682        theTria2->GetType() != SMDSAbs_Face )
683     return false;
684
685   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
686       (theTria2->GetEntityType() == SMDSEntity_Triangle))
687   {
688     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
689     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
690     //    |/ |                                         | \|
691     //  B +--+ 2                                     B +--+ 2
692
693     // put nodes in array and find out indices of the same ones
694     const SMDS_MeshNode* aNodes [6];
695     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
696     int i = 0;
697     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
698     while ( it->more() ) {
699       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
700
701       if ( i > 2 ) // theTria2
702         // find same node of theTria1
703         for ( int j = 0; j < 3; j++ )
704           if ( aNodes[ i ] == aNodes[ j ]) {
705             sameInd[ j ] = i;
706             sameInd[ i ] = j;
707             break;
708           }
709       // next
710       i++;
711       if ( i == 3 ) {
712         if ( it->more() )
713           return false; // theTria1 is not a triangle
714         it = theTria2->nodesIterator();
715       }
716       if ( i == 6 && it->more() )
717         return false; // theTria2 is not a triangle
718     }
719
720     // find indices of 1,2 and of A,B in theTria1
721     int iA = -1, iB = 0, i1 = 0, i2 = 0;
722     for ( i = 0; i < 6; i++ ) {
723       if ( sameInd [ i ] == -1 ) {
724         if ( i < 3 ) i1 = i;
725         else         i2 = i;
726       }
727       else if (i < 3) {
728         if ( iA >= 0) iB = i;
729         else          iA = i;
730       }
731     }
732     // nodes 1 and 2 should not be the same
733     if ( aNodes[ i1 ] == aNodes[ i2 ] )
734       return false;
735
736     // theTria1: A->2
737     aNodes[ iA ] = aNodes[ i2 ];
738     // theTria2: B->1
739     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
740
741     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
742     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
743
744     return true;
745
746   } // end if(F1 && F2)
747
748   // check case of quadratic faces
749   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
750       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
751     return false;
752   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
753       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
754     return false;
755
756   //       5
757   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
758   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
759   //    |   / |
760   //  7 +  +  + 6
761   //    | /9  |
762   //    |/    |
763   //  4 +--+--+ 3
764   //       8
765
766   vector< const SMDS_MeshNode* > N1;
767   vector< const SMDS_MeshNode* > N2;
768   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
769     return false;
770   // now we receive following N1 and N2 (using numeration as above image)
771   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
772   // i.e. first nodes from both arrays determ new diagonal
773
774   vector< const SMDS_MeshNode*> N1new( N1.size() );
775   vector< const SMDS_MeshNode*> N2new( N2.size() );
776   N1new.back() = N1.back(); // central node of biquadratic
777   N2new.back() = N2.back();
778   N1new[0] = N1[0];  N2new[0] = N1[0];
779   N1new[1] = N2[0];  N2new[1] = N1[1];
780   N1new[2] = N2[1];  N2new[2] = N2[0];
781   N1new[3] = N1[4];  N2new[3] = N1[3];
782   N1new[4] = N2[3];  N2new[4] = N2[5];
783   N1new[5] = N1[5];  N2new[5] = N1[4];
784   // change nodes in faces
785   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
786   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
787
788   // move the central node of biquadratic triangle
789   SMESH_MesherHelper helper( *GetMesh() );
790   for ( int is2nd = 0; is2nd < 2; ++is2nd )
791   {
792     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
793     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
794     if ( nodes.size() < 7 )
795       continue;
796     helper.SetSubShape( tria->getshapeId() );
797     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
798     gp_Pnt xyz;
799     if ( F.IsNull() )
800     {
801       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
802               SMESH_NodeXYZ( nodes[4] ) +
803               SMESH_NodeXYZ( nodes[5] )) / 3.;
804     }
805     else
806     {
807       bool checkUV;
808       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
809                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
810                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
811       TopLoc_Location loc;
812       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
813       xyz = S->Value( uv.X(), uv.Y() );
814       xyz.Transform( loc );
815       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
816            nodes[6]->getshapeId() > 0 )
817         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
818     }
819     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
820   }
821   return true;
822 }
823
824 //=======================================================================
825 //function : findTriangles
826 //purpose  : find triangles sharing theNode1-theNode2 link
827 //=======================================================================
828
829 static bool findTriangles(const SMDS_MeshNode *    theNode1,
830                           const SMDS_MeshNode *    theNode2,
831                           const SMDS_MeshElement*& theTria1,
832                           const SMDS_MeshElement*& theTria2)
833 {
834   if ( !theNode1 || !theNode2 ) return false;
835
836   theTria1 = theTria2 = 0;
837
838   set< const SMDS_MeshElement* > emap;
839   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
840   while (it->more()) {
841     const SMDS_MeshElement* elem = it->next();
842     if ( elem->NbCornerNodes() == 3 )
843       emap.insert( elem );
844   }
845   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
846   while (it->more()) {
847     const SMDS_MeshElement* elem = it->next();
848     if ( emap.count( elem )) {
849       if ( !theTria1 )
850       {
851         theTria1 = elem;
852       }
853       else  
854       {
855         theTria2 = elem;
856         // theTria1 must be element with minimum ID
857         if ( theTria2->GetID() < theTria1->GetID() )
858           std::swap( theTria2, theTria1 );
859         return true;
860       }
861     }
862   }
863   return false;
864 }
865
866 //=======================================================================
867 //function : InverseDiag
868 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
869 //           with ones built on the same 4 nodes but having other common link.
870 //           Return false if proper faces not found
871 //=======================================================================
872
873 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
874                                     const SMDS_MeshNode * theNode2)
875 {
876   ClearLastCreated();
877
878   const SMDS_MeshElement *tr1, *tr2;
879   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
880     return false;
881
882   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
883        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
884     return false;
885
886   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
887       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
888
889     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
890     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
891     //    |/ |                                    | \|
892     //  B +--+ 2                                B +--+ 2
893
894     // put nodes in array
895     // and find indices of 1,2 and of A in tr1 and of B in tr2
896     int i, iA1 = 0, i1 = 0;
897     const SMDS_MeshNode* aNodes1 [3];
898     SMDS_ElemIteratorPtr it;
899     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
900       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
901       if ( aNodes1[ i ] == theNode1 )
902         iA1 = i; // node A in tr1
903       else if ( aNodes1[ i ] != theNode2 )
904         i1 = i;  // node 1
905     }
906     int iB2 = 0, i2 = 0;
907     const SMDS_MeshNode* aNodes2 [3];
908     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
909       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
910       if ( aNodes2[ i ] == theNode2 )
911         iB2 = i; // node B in tr2
912       else if ( aNodes2[ i ] != theNode1 )
913         i2 = i;  // node 2
914     }
915
916     // nodes 1 and 2 should not be the same
917     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
918       return false;
919
920     // tr1: A->2
921     aNodes1[ iA1 ] = aNodes2[ i2 ];
922     // tr2: B->1
923     aNodes2[ iB2 ] = aNodes1[ i1 ];
924
925     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
926     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
927
928     return true;
929   }
930
931   // check case of quadratic faces
932   return InverseDiag(tr1,tr2);
933 }
934
935 //=======================================================================
936 //function : getQuadrangleNodes
937 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
938 //           fusion of triangles tr1 and tr2 having shared link on
939 //           theNode1 and theNode2
940 //=======================================================================
941
942 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
943                         const SMDS_MeshNode *    theNode1,
944                         const SMDS_MeshNode *    theNode2,
945                         const SMDS_MeshElement * tr1,
946                         const SMDS_MeshElement * tr2 )
947 {
948   if( tr1->NbNodes() != tr2->NbNodes() )
949     return false;
950   // find the 4-th node to insert into tr1
951   const SMDS_MeshNode* n4 = 0;
952   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
953   int i=0;
954   while ( !n4 && i<3 ) {
955     const SMDS_MeshNode * n = cast2Node( it->next() );
956     i++;
957     bool isDiag = ( n == theNode1 || n == theNode2 );
958     if ( !isDiag )
959       n4 = n;
960   }
961   // Make an array of nodes to be in a quadrangle
962   int iNode = 0, iFirstDiag = -1;
963   it = tr1->nodesIterator();
964   i=0;
965   while ( i<3 ) {
966     const SMDS_MeshNode * n = cast2Node( it->next() );
967     i++;
968     bool isDiag = ( n == theNode1 || n == theNode2 );
969     if ( isDiag ) {
970       if ( iFirstDiag < 0 )
971         iFirstDiag = iNode;
972       else if ( iNode - iFirstDiag == 1 )
973         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
974     }
975     else if ( n == n4 ) {
976       return false; // tr1 and tr2 should not have all the same nodes
977     }
978     theQuadNodes[ iNode++ ] = n;
979   }
980   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
981     theQuadNodes[ iNode ] = n4;
982
983   return true;
984 }
985
986 //=======================================================================
987 //function : DeleteDiag
988 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
989 //           with a quadrangle built on the same 4 nodes.
990 //           Return false if proper faces not found
991 //=======================================================================
992
993 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
994                                    const SMDS_MeshNode * theNode2)
995 {
996   ClearLastCreated();
997
998   const SMDS_MeshElement *tr1, *tr2;
999   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1000     return false;
1001
1002   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1003        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1004     return false;
1005
1006   SMESHDS_Mesh * aMesh = GetMeshDS();
1007
1008   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1009       (tr2->GetEntityType() == SMDSEntity_Triangle))
1010   {
1011     const SMDS_MeshNode* aNodes [ 4 ];
1012     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1013       return false;
1014
1015     const SMDS_MeshElement* newElem = 0;
1016     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1017     myLastCreatedElems.push_back(newElem);
1018     AddToSameGroups( newElem, tr1, aMesh );
1019     int aShapeId = tr1->getshapeId();
1020     if ( aShapeId )
1021       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1022
1023     aMesh->RemoveElement( tr1 );
1024     aMesh->RemoveElement( tr2 );
1025
1026     return true;
1027   }
1028
1029   // check case of quadratic faces
1030   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1031     return false;
1032   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1033     return false;
1034
1035   //       5
1036   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1037   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1038   //    |   / |
1039   //  7 +  +  + 6
1040   //    | /9  |
1041   //    |/    |
1042   //  4 +--+--+ 3
1043   //       8
1044
1045   vector< const SMDS_MeshNode* > N1;
1046   vector< const SMDS_MeshNode* > N2;
1047   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1048     return false;
1049   // now we receive following N1 and N2 (using numeration as above image)
1050   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1051   // i.e. first nodes from both arrays determ new diagonal
1052
1053   const SMDS_MeshNode* aNodes[8];
1054   aNodes[0] = N1[0];
1055   aNodes[1] = N1[1];
1056   aNodes[2] = N2[0];
1057   aNodes[3] = N2[1];
1058   aNodes[4] = N1[3];
1059   aNodes[5] = N2[5];
1060   aNodes[6] = N2[3];
1061   aNodes[7] = N1[5];
1062
1063   const SMDS_MeshElement* newElem = 0;
1064   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1065                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1066   myLastCreatedElems.push_back(newElem);
1067   AddToSameGroups( newElem, tr1, aMesh );
1068   int aShapeId = tr1->getshapeId();
1069   if ( aShapeId )
1070   {
1071     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1072   }
1073   aMesh->RemoveElement( tr1 );
1074   aMesh->RemoveElement( tr2 );
1075
1076   // remove middle node (9)
1077   GetMeshDS()->RemoveNode( N1[4] );
1078
1079   return true;
1080 }
1081
1082 //=======================================================================
1083 //function : Reorient
1084 //purpose  : Reverse theElement orientation
1085 //=======================================================================
1086
1087 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1088 {
1089   ClearLastCreated();
1090
1091   if (!theElem)
1092     return false;
1093   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1094   if ( !it || !it->more() )
1095     return false;
1096
1097   const SMDSAbs_ElementType type = theElem->GetType();
1098   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1099     return false;
1100
1101   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1102   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1103   {
1104     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1105     if (!aPolyedre) {
1106       MESSAGE("Warning: bad volumic element");
1107       return false;
1108     }
1109     SMDS_VolumeTool vTool( aPolyedre );
1110     const int nbFaces = vTool.NbFaces();
1111     vector<int> quantities( nbFaces );
1112     vector<const SMDS_MeshNode *> poly_nodes;
1113
1114     // check if all facets are oriented equally
1115     bool sameOri = true;
1116     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1117     for (int iface = 0; iface < nbFaces; iface++)
1118     {
1119       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1120       if ( facetOri[ iface ] != facetOri[ 0 ])
1121         sameOri = false;
1122     }
1123
1124     // reverse faces of the polyhedron
1125     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1126     poly_nodes.reserve( vTool.NbNodes() );
1127     for ( int iface = 0; iface < nbFaces; iface++ )
1128     {
1129       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1130       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1131       bool toReverse = ( facetOri[ iface ] != neededOri );
1132
1133       quantities[ iface ] = nbFaceNodes;
1134
1135       if ( toReverse )
1136         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1137           poly_nodes.push_back( nodes[ inode ]);
1138       else
1139         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1140     }
1141     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1142   }
1143   else // other elements
1144   {
1145     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1146     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1147     if ( interlace.empty() )
1148     {
1149       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1150     }
1151     else
1152     {
1153       SMDS_MeshCell::applyInterlace( interlace, nodes );
1154     }
1155     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1156   }
1157   return false;
1158 }
1159
1160 //================================================================================
1161 /*!
1162  * \brief Reorient faces.
1163  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1164  * \param theDirection - desired direction of normal of \a theFace
1165  * \param theFace - one of \a theFaces that should be oriented according to
1166  *        \a theDirection and whose orientation defines orientation of other faces
1167  * \return number of reoriented faces.
1168  */
1169 //================================================================================
1170
1171 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1172                                   const gp_Dir&            theDirection,
1173                                   const SMDS_MeshElement * theFace)
1174 {
1175   int nbReori = 0;
1176   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1177
1178   if ( theFaces.empty() )
1179   {
1180     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1181     while ( fIt->more() )
1182       theFaces.insert( theFaces.end(), fIt->next() );
1183   }
1184
1185   // orient theFace according to theDirection
1186   gp_XYZ normal;
1187   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1188   if ( normal * theDirection.XYZ() < 0 )
1189     nbReori += Reorient( theFace );
1190
1191   // Orient other faces
1192
1193   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1194   TIDSortedElemSet avoidSet;
1195   set< SMESH_TLink > checkedLinks;
1196   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1197
1198   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1199     theFaces.erase( theFace );
1200   startFaces.insert( theFace );
1201
1202   int nodeInd1, nodeInd2;
1203   const SMDS_MeshElement*           otherFace;
1204   vector< const SMDS_MeshElement* > facesNearLink;
1205   vector< std::pair< int, int > >   nodeIndsOfFace;
1206
1207   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1208   while ( !startFaces.empty() )
1209   {
1210     startFace = startFaces.begin();
1211     theFace = *startFace;
1212     startFaces.erase( startFace );
1213     if ( !visitedFaces.insert( theFace ).second )
1214       continue;
1215
1216     avoidSet.clear();
1217     avoidSet.insert(theFace);
1218
1219     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1220
1221     const int nbNodes = theFace->NbCornerNodes();
1222     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1223     {
1224       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1225       linkIt_isNew = checkedLinks.insert( link );
1226       if ( !linkIt_isNew.second )
1227       {
1228         // link has already been checked and won't be encountered more
1229         // if the group (theFaces) is manifold
1230         //checkedLinks.erase( linkIt_isNew.first );
1231       }
1232       else
1233       {
1234         facesNearLink.clear();
1235         nodeIndsOfFace.clear();
1236         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1237                                                              theFaces, avoidSet,
1238                                                              &nodeInd1, &nodeInd2 )))
1239           if ( otherFace != theFace)
1240           {
1241             facesNearLink.push_back( otherFace );
1242             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1243             avoidSet.insert( otherFace );
1244           }
1245         if ( facesNearLink.size() > 1 )
1246         {
1247           // NON-MANIFOLD mesh shell !
1248           // select a face most co-directed with theFace,
1249           // other faces won't be visited this time
1250           gp_XYZ NF, NOF;
1251           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1252           double proj, maxProj = -1;
1253           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1254             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1255             if (( proj = Abs( NF * NOF )) > maxProj ) {
1256               maxProj = proj;
1257               otherFace = facesNearLink[i];
1258               nodeInd1  = nodeIndsOfFace[i].first;
1259               nodeInd2  = nodeIndsOfFace[i].second;
1260             }
1261           }
1262           // not to visit rejected faces
1263           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1264             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1265               visitedFaces.insert( facesNearLink[i] );
1266         }
1267         else if ( facesNearLink.size() == 1 )
1268         {
1269           otherFace = facesNearLink[0];
1270           nodeInd1  = nodeIndsOfFace.back().first;
1271           nodeInd2  = nodeIndsOfFace.back().second;
1272         }
1273         if ( otherFace && otherFace != theFace)
1274         {
1275           // link must be reverse in otherFace if orientation to otherFace
1276           // is same as that of theFace
1277           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1278           {
1279             nbReori += Reorient( otherFace );
1280           }
1281           startFaces.insert( otherFace );
1282         }
1283       }
1284       std::swap( link.first, link.second ); // reverse the link
1285     }
1286   }
1287   return nbReori;
1288 }
1289
1290 //================================================================================
1291 /*!
1292  * \brief Reorient faces basing on orientation of adjacent volumes.
1293  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1294  * \param theVolumes - reference volumes.
1295  * \param theOutsideNormal - to orient faces to have their normal
1296  *        pointing either \a outside or \a inside the adjacent volumes.
1297  * \return number of reoriented faces.
1298  */
1299 //================================================================================
1300
1301 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1302                                       TIDSortedElemSet & theVolumes,
1303                                       const bool         theOutsideNormal)
1304 {
1305   int nbReori = 0;
1306
1307   SMDS_ElemIteratorPtr faceIt;
1308   if ( theFaces.empty() )
1309     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1310   else
1311     faceIt = SMESHUtils::elemSetIterator( theFaces );
1312
1313   vector< const SMDS_MeshNode* > faceNodes;
1314   TIDSortedElemSet checkedVolumes;
1315   set< const SMDS_MeshNode* > faceNodesSet;
1316   SMDS_VolumeTool volumeTool;
1317
1318   while ( faceIt->more() ) // loop on given faces
1319   {
1320     const SMDS_MeshElement* face = faceIt->next();
1321     if ( face->GetType() != SMDSAbs_Face )
1322       continue;
1323
1324     const size_t nbCornersNodes = face->NbCornerNodes();
1325     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1326
1327     checkedVolumes.clear();
1328     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1329     while ( vIt->more() )
1330     {
1331       const SMDS_MeshElement* volume = vIt->next();
1332
1333       if ( !checkedVolumes.insert( volume ).second )
1334         continue;
1335       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336         continue;
1337
1338       // is volume adjacent?
1339       bool allNodesCommon = true;
1340       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1341         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1342       if ( !allNodesCommon )
1343         continue;
1344
1345       // get nodes of a corresponding volume facet
1346       faceNodesSet.clear();
1347       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1348       volumeTool.Set( volume );
1349       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1350       if ( facetID < 0 ) continue;
1351       volumeTool.SetExternalNormal();
1352       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1353
1354       // compare order of faceNodes and facetNodes
1355       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1356       int iNN[2];
1357       for ( int i = 0; i < 2; ++i )
1358       {
1359         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1360         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1361           if ( faceNodes[ iN ] == n )
1362           {
1363             iNN[ i ] = iN;
1364             break;
1365           }
1366       }
1367       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1368       if ( isOutside != theOutsideNormal )
1369         nbReori += Reorient( face );
1370     }
1371   }  // loop on given faces
1372
1373   return nbReori;
1374 }
1375
1376 //=======================================================================
1377 //function : getBadRate
1378 //purpose  :
1379 //=======================================================================
1380
1381 static double getBadRate (const SMDS_MeshElement*               theElem,
1382                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1383 {
1384   SMESH::Controls::TSequenceOfXYZ P;
1385   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1386     return 1e100;
1387   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1388   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 }
1390
1391 //=======================================================================
1392 //function : QuadToTri
1393 //purpose  : Cut quadrangles into triangles.
1394 //           theCrit is used to select a diagonal to cut
1395 //=======================================================================
1396
1397 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1398                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1399 {
1400   ClearLastCreated();
1401
1402   if ( !theCrit.get() )
1403     return false;
1404
1405   SMESHDS_Mesh *       aMesh = GetMeshDS();
1406   Handle(Geom_Surface) surface;
1407   SMESH_MesherHelper   helper( *GetMesh() );
1408
1409   myLastCreatedElems.reserve( theElems.size() * 2 );
1410
1411   TIDSortedElemSet::iterator itElem;
1412   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1413   {
1414     const SMDS_MeshElement* elem = *itElem;
1415     if ( !elem || elem->GetType() != SMDSAbs_Face )
1416       continue;
1417     if ( elem->NbCornerNodes() != 4 )
1418       continue;
1419
1420     // retrieve element nodes
1421     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1422
1423     // compare two sets of possible triangles
1424     double aBadRate1, aBadRate2; // to what extent a set is bad
1425     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1426     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1427     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1428
1429     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1430     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1431     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1432
1433     const int aShapeId = FindShape( elem );
1434     const SMDS_MeshElement* newElem1 = 0;
1435     const SMDS_MeshElement* newElem2 = 0;
1436
1437     if ( !elem->IsQuadratic() ) // split linear quadrangle
1438     {
1439       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1440       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1441       if ( aBadRate1 <= aBadRate2 ) {
1442         // tr1 + tr2 is better
1443         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1444         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445       }
1446       else {
1447         // tr3 + tr4 is better
1448         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1449         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450       }
1451     }
1452     else // split quadratic quadrangle
1453     {
1454       helper.SetIsQuadratic( true );
1455       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1456
1457       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1458       if ( aNodes.size() == 9 )
1459       {
1460         helper.SetIsBiQuadratic( true );
1461         if ( aBadRate1 <= aBadRate2 )
1462           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1463         else
1464           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1465       }
1466       // create a new element
1467       if ( aBadRate1 <= aBadRate2 ) {
1468         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1469         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470       }
1471       else {
1472         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1473         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1474       }
1475     } // quadratic case
1476
1477     // care of a new element
1478
1479     myLastCreatedElems.push_back(newElem1);
1480     myLastCreatedElems.push_back(newElem2);
1481     AddToSameGroups( newElem1, elem, aMesh );
1482     AddToSameGroups( newElem2, elem, aMesh );
1483
1484     // put a new triangle on the same shape
1485     if ( aShapeId )
1486       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1487     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1488
1489     aMesh->RemoveElement( elem );
1490   }
1491   return true;
1492 }
1493
1494 //=======================================================================
1495 /*!
1496  * \brief Split each of given quadrangles into 4 triangles.
1497  * \param theElems - The faces to be split. If empty all faces are split.
1498  */
1499 //=======================================================================
1500
1501 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1502 {
1503   ClearLastCreated();
1504   myLastCreatedElems.reserve( theElems.size() * 4 );
1505
1506   SMESH_MesherHelper helper( *GetMesh() );
1507   helper.SetElementsOnShape( true );
1508
1509   // get standalone groups of faces
1510   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1511   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1512     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1513       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1514         allFaceGroups.push_back( & group->SMDSGroup() );
1515
1516   bool   checkUV;
1517   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1518   gp_XYZ xyz[9];
1519   vector< const SMDS_MeshNode* > nodes;
1520   SMESHDS_SubMesh*               subMeshDS = 0;
1521   TopoDS_Face                    F;
1522   Handle(Geom_Surface)           surface;
1523   TopLoc_Location                loc;
1524
1525   SMDS_ElemIteratorPtr faceIt;
1526   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1527   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1528
1529   while ( faceIt->more() )
1530   {
1531     const SMDS_MeshElement* quad = faceIt->next();
1532     if ( !quad || quad->NbCornerNodes() != 4 )
1533       continue;
1534
1535     // get a surface the quad is on
1536
1537     if ( quad->getshapeId() < 1 )
1538     {
1539       F.Nullify();
1540       helper.SetSubShape( 0 );
1541       subMeshDS = 0;
1542     }
1543     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1544     {
1545       helper.SetSubShape( quad->getshapeId() );
1546       if ( !helper.GetSubShape().IsNull() &&
1547            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1548       {
1549         F = TopoDS::Face( helper.GetSubShape() );
1550         surface = BRep_Tool::Surface( F, loc );
1551         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552       }
1553       else
1554       {
1555         helper.SetSubShape( 0 );
1556         subMeshDS = 0;
1557       }
1558     }
1559
1560     // create a central node
1561
1562     const SMDS_MeshNode* nCentral;
1563     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1564
1565     if ( nodes.size() == 9 )
1566     {
1567       nCentral = nodes.back();
1568     }
1569     else
1570     {
1571       size_t iN = 0;
1572       if ( F.IsNull() )
1573       {
1574         for ( ; iN < nodes.size(); ++iN )
1575           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1576
1577         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1579
1580         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581                                    xyz[0], xyz[1], xyz[2], xyz[3],
1582                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1583       }
1584       else
1585       {
1586         for ( ; iN < nodes.size(); ++iN )
1587           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1588
1589         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1591
1592         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593                                   uv[0], uv[1], uv[2], uv[3],
1594                                   uv[4], uv[5], uv[6], uv[7] );
1595
1596         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597         xyz[ 8 ] = p.XYZ();
1598       }
1599
1600       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601                                  uv[8].X(), uv[8].Y() );
1602       myLastCreatedNodes.push_back( nCentral );
1603     }
1604
1605     helper.SetIsQuadratic  ( nodes.size() > 4 );
1606     helper.SetIsBiQuadratic( nodes.size() == 9 );
1607     if ( helper.GetIsQuadratic() )
1608       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609
1610     // select groups to update
1611     faceGroups.clear();
1612     for ( SMDS_MeshGroup* group : allFaceGroups )
1613       if ( group->Remove( quad ))
1614         faceGroups.push_back( group );
1615
1616     // create 4 triangles
1617
1618     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1619
1620     for ( int i = 0; i < 4; ++i )
1621     {
1622       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1623                                                nodes[(i+1)%4],
1624                                                nCentral );
1625       myLastCreatedElems.push_back( tria );
1626       for ( SMDS_MeshGroup* group : faceGroups )
1627         group->Add( tria );
1628     }
1629   }
1630 }
1631
1632 //=======================================================================
1633 //function : BestSplit
1634 //purpose  : Find better diagonal for cutting.
1635 //=======================================================================
1636
1637 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1638                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1639 {
1640   ClearLastCreated();
1641
1642   if (!theCrit.get())
1643     return -1;
1644
1645   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1646     return -1;
1647
1648   if( theQuad->NbNodes()==4 ||
1649       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1650
1651     // retrieve element nodes
1652     const SMDS_MeshNode* aNodes [4];
1653     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1654     int i = 0;
1655     //while (itN->more())
1656     while (i<4) {
1657       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1658     }
1659     // compare two sets of possible triangles
1660     double aBadRate1, aBadRate2; // to what extent a set is bad
1661     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1662     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1663     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1664
1665     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1666     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1667     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1668     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1669     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1670     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1671       return 1; // diagonal 1-3
1672
1673     return 2; // diagonal 2-4
1674   }
1675   return -1;
1676 }
1677
1678 namespace
1679 {
1680   // Methods of splitting volumes into tetra
1681
1682   const int theHexTo5_1[5*4+1] =
1683     {
1684       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1685     };
1686   const int theHexTo5_2[5*4+1] =
1687     {
1688       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1689     };
1690   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1691
1692   const int theHexTo6_1[6*4+1] =
1693     {
1694       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1695     };
1696   const int theHexTo6_2[6*4+1] =
1697     {
1698       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1699     };
1700   const int theHexTo6_3[6*4+1] =
1701     {
1702       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1703     };
1704   const int theHexTo6_4[6*4+1] =
1705     {
1706       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1707     };
1708   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1709
1710   const int thePyraTo2_1[2*4+1] =
1711     {
1712       0, 1, 2, 4,    0, 2, 3, 4,   -1
1713     };
1714   const int thePyraTo2_2[2*4+1] =
1715     {
1716       1, 2, 3, 4,    1, 3, 0, 4,   -1
1717     };
1718   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1719
1720   const int thePentaTo3_1[3*4+1] =
1721     {
1722       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1723     };
1724   const int thePentaTo3_2[3*4+1] =
1725     {
1726       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1727     };
1728   const int thePentaTo3_3[3*4+1] =
1729     {
1730       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1731     };
1732   const int thePentaTo3_4[3*4+1] =
1733     {
1734       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1735     };
1736   const int thePentaTo3_5[3*4+1] =
1737     {
1738       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1739     };
1740   const int thePentaTo3_6[3*4+1] =
1741     {
1742       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1743     };
1744   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1745                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1746
1747   // Methods of splitting hexahedron into prisms
1748
1749   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1750     {
1751       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1752     };
1753   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1754     {
1755       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1756     };
1757   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1758     {
1759       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1760     };
1761
1762   const int theHexTo2Prisms_BT_1[6*2+1] =
1763     {
1764       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1765     };
1766   const int theHexTo2Prisms_BT_2[6*2+1] =
1767     {
1768       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1769     };
1770   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1771
1772   const int theHexTo2Prisms_LR_1[6*2+1] =
1773     {
1774       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1775     };
1776   const int theHexTo2Prisms_LR_2[6*2+1] =
1777     {
1778       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1779     };
1780   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1781
1782   const int theHexTo2Prisms_FB_1[6*2+1] =
1783     {
1784       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1785     };
1786   const int theHexTo2Prisms_FB_2[6*2+1] =
1787     {
1788       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1789     };
1790   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1791
1792
1793   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1794   {
1795     int _n1, _n2, _n3;
1796     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1797     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1798     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1799                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1800   };
1801   struct TSplitMethod
1802   {
1803     int        _nbSplits;
1804     int        _nbCorners;
1805     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1806     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1807     bool       _ownConn;      //!< to delete _connectivity in destructor
1808     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1809
1810     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1811       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1812     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1813     TSplitMethod(const TSplitMethod &splitMethod)
1814       : _nbSplits(splitMethod._nbSplits),
1815         _nbCorners(splitMethod._nbCorners),
1816         _baryNode(splitMethod._baryNode),
1817         _ownConn(splitMethod._ownConn),
1818         _faceBaryNode(splitMethod._faceBaryNode)
1819     {
1820       _connectivity = splitMethod._connectivity;
1821       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1822       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1823     }
1824     bool hasFacet( const TTriangleFacet& facet ) const
1825     {
1826       if ( _nbCorners == 4 )
1827       {
1828         const int* tetConn = _connectivity;
1829         for ( ; tetConn[0] >= 0; tetConn += 4 )
1830           if (( facet.contains( tetConn[0] ) +
1831                 facet.contains( tetConn[1] ) +
1832                 facet.contains( tetConn[2] ) +
1833                 facet.contains( tetConn[3] )) == 3 )
1834             return true;
1835       }
1836       else // prism, _nbCorners == 6
1837       {
1838         const int* prismConn = _connectivity;
1839         for ( ; prismConn[0] >= 0; prismConn += 6 )
1840         {
1841           if (( facet.contains( prismConn[0] ) &&
1842                 facet.contains( prismConn[1] ) &&
1843                 facet.contains( prismConn[2] ))
1844               ||
1845               ( facet.contains( prismConn[3] ) &&
1846                 facet.contains( prismConn[4] ) &&
1847                 facet.contains( prismConn[5] )))
1848             return true;
1849         }
1850       }
1851       return false;
1852     }
1853   };
1854
1855   //=======================================================================
1856   /*!
1857    * \brief return TSplitMethod for the given element to split into tetrahedra
1858    */
1859   //=======================================================================
1860
1861   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1862   {
1863     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1864
1865     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1866     // an edge and a face barycenter; tertaherdons are based on triangles and
1867     // a volume barycenter
1868     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1869
1870     // Find out how adjacent volumes are split
1871
1872     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1873     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1874     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1875     {
1876       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1877       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1878       if ( nbNodes < 4 ) continue;
1879
1880       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1881       const int* nInd = vol.GetFaceNodesIndices( iF );
1882       if ( nbNodes == 4 )
1883       {
1884         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1885         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1886         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1887         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1888       }
1889       else
1890       {
1891         int iCom = 0; // common node of triangle faces to split into
1892         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1893         {
1894           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1895                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1896                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1897           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1898                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1899                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1900           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1901           {
1902             triaSplits.push_back( t012 );
1903             triaSplits.push_back( t023 );
1904             break;
1905           }
1906         }
1907       }
1908       if ( !triaSplits.empty() )
1909         hasAdjacentSplits = true;
1910     }
1911
1912     // Among variants of split method select one compliant with adjacent volumes
1913
1914     TSplitMethod method;
1915     if ( !vol.Element()->IsPoly() && !is24TetMode )
1916     {
1917       int nbVariants = 2, nbTet = 0;
1918       const int** connVariants = 0;
1919       switch ( vol.Element()->GetEntityType() )
1920       {
1921       case SMDSEntity_Hexa:
1922       case SMDSEntity_Quad_Hexa:
1923       case SMDSEntity_TriQuad_Hexa:
1924         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1925           connVariants = theHexTo5, nbTet = 5;
1926         else
1927           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1928         break;
1929       case SMDSEntity_Pyramid:
1930       case SMDSEntity_Quad_Pyramid:
1931         connVariants = thePyraTo2;  nbTet = 2;
1932         break;
1933       case SMDSEntity_Penta:
1934       case SMDSEntity_Quad_Penta:
1935       case SMDSEntity_BiQuad_Penta:
1936         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1937         break;
1938       default:
1939         nbVariants = 0;
1940       }
1941       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1942       {
1943         // check method compliance with adjacent tetras,
1944         // all found splits must be among facets of tetras described by this method
1945         method = TSplitMethod( nbTet, connVariants[variant] );
1946         if ( hasAdjacentSplits && method._nbSplits > 0 )
1947         {
1948           bool facetCreated = true;
1949           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1950           {
1951             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1952             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1953               facetCreated = method.hasFacet( *facet );
1954           }
1955           if ( !facetCreated )
1956             method = TSplitMethod(0); // incompatible method
1957         }
1958       }
1959     }
1960     if ( method._nbSplits < 1 )
1961     {
1962       // No standard method is applicable, use a generic solution:
1963       // each facet of a volume is split into triangles and
1964       // each of triangles and a volume barycenter form a tetrahedron.
1965
1966       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1967
1968       int* connectivity = new int[ maxTetConnSize + 1 ];
1969       method._connectivity = connectivity;
1970       method._ownConn = true;
1971       method._baryNode = !isHex27; // to create central node or not
1972
1973       int connSize = 0;
1974       int baryCenInd = vol.NbNodes() - int( isHex27 );
1975       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1976       {
1977         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1978         const int*   nInd = vol.GetFaceNodesIndices( iF );
1979         // find common node of triangle facets of tetra to create
1980         int iCommon = 0; // index in linear numeration
1981         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1982         if ( !triaSplits.empty() )
1983         {
1984           // by found facets
1985           const TTriangleFacet* facet = &triaSplits.front();
1986           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1987             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1988                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1989               break;
1990         }
1991         else if ( nbNodes > 3 && !is24TetMode )
1992         {
1993           // find the best method of splitting into triangles by aspect ratio
1994           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1995           map< double, int > badness2iCommon;
1996           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1997           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1998           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1999           {
2000             double badness = 0;
2001             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2002             {
2003               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2004                                       nodes[ iQ*((iLast-1)%nbNodes)],
2005                                       nodes[ iQ*((iLast  )%nbNodes)]);
2006               badness += getBadRate( &tria, aspectRatio );
2007             }
2008             badness2iCommon.insert( make_pair( badness, iCommon ));
2009           }
2010           // use iCommon with lowest badness
2011           iCommon = badness2iCommon.begin()->second;
2012         }
2013         if ( iCommon >= nbNodes )
2014           iCommon = 0; // something wrong
2015
2016         // fill connectivity of tetrahedra based on a current face
2017         int nbTet = nbNodes - 2;
2018         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2019         {
2020           int faceBaryCenInd;
2021           if ( isHex27 )
2022           {
2023             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2024             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2025           }
2026           else
2027           {
2028             method._faceBaryNode[ iF ] = 0;
2029             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2030           }
2031           nbTet = nbNodes;
2032           for ( int i = 0; i < nbTet; ++i )
2033           {
2034             int i1 = i, i2 = (i+1) % nbNodes;
2035             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2036             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038             connectivity[ connSize++ ] = faceBaryCenInd;
2039             connectivity[ connSize++ ] = baryCenInd;
2040           }
2041         }
2042         else
2043         {
2044           for ( int i = 0; i < nbTet; ++i )
2045           {
2046             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2047             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2048             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2049             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2050             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2051             connectivity[ connSize++ ] = baryCenInd;
2052           }
2053         }
2054         method._nbSplits += nbTet;
2055
2056       } // loop on volume faces
2057
2058       connectivity[ connSize++ ] = -1;
2059
2060     } // end of generic solution
2061
2062     return method;
2063   }
2064   //=======================================================================
2065   /*!
2066    * \brief return TSplitMethod to split haxhedron into prisms
2067    */
2068   //=======================================================================
2069
2070   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2071                                     const int        methodFlags,
2072                                     const int        facetToSplit)
2073   {
2074     TSplitMethod method;
2075
2076     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2077     // B, T, L, B, R, F
2078     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2079
2080     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2081     {
2082       static TSplitMethod to4methods[4]; // order BT, LR, FB
2083       if ( to4methods[iF]._nbSplits == 0 )
2084       {
2085         switch ( iF ) {
2086         case 0:
2087           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2088           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2089           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2090           break;
2091         case 1:
2092           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2093           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2094           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2095           break;
2096         case 2:
2097           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2098           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2099           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2100           break;
2101         default: return to4methods[3];
2102         }
2103         to4methods[iF]._nbSplits  = 4;
2104         to4methods[iF]._nbCorners = 6;
2105       }
2106       method = to4methods[iF];
2107       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2108       return method;
2109     }
2110     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2111
2112     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2113
2114     const int nbVariants = 2, nbSplits = 2;
2115     const int** connVariants = 0;
2116     switch ( iF ) {
2117     case 0: connVariants = theHexTo2Prisms_BT; break;
2118     case 1: connVariants = theHexTo2Prisms_LR; break;
2119     case 2: connVariants = theHexTo2Prisms_FB; break;
2120     default: return method;
2121     }
2122
2123     // look for prisms adjacent via facetToSplit and an opposite one
2124     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2125     {
2126       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2128       if ( nbNodes != 4 ) return method;
2129
2130       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2131       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2132       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2133       TTriangleFacet* t;
2134       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2135         t = &t012;
2136       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2137         t = &t123;
2138       else
2139         continue;
2140
2141       // there are adjacent prism
2142       for ( int variant = 0; variant < nbVariants; ++variant )
2143       {
2144         // check method compliance with adjacent prisms,
2145         // the found prism facets must be among facets of prisms described by current method
2146         method._nbSplits     = nbSplits;
2147         method._nbCorners    = 6;
2148         method._connectivity = connVariants[ variant ];
2149         if ( method.hasFacet( *t ))
2150           return method;
2151       }
2152     }
2153
2154     // No adjacent prisms. Select a variant with a best aspect ratio.
2155
2156     double badness[2] = { 0., 0. };
2157     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2158     const SMDS_MeshNode** nodes = vol.GetNodes();
2159     for ( int variant = 0; variant < nbVariants; ++variant )
2160       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2161       {
2162         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2163         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2164
2165         method._connectivity = connVariants[ variant ];
2166         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2167         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2168         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2169
2170         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2171                                 nodes[ t->_n2 ],
2172                                 nodes[ t->_n3 ] );
2173         badness[ variant ] += getBadRate( &tria, aspectRatio );
2174       }
2175     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2176
2177     method._nbSplits     = nbSplits;
2178     method._nbCorners    = 6;
2179     method._connectivity = connVariants[ iBetter ];
2180
2181     return method;
2182   }
2183
2184   //================================================================================
2185   /*!
2186    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2187    */
2188   //================================================================================
2189
2190   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2191                                        const SMDSAbs_GeometryType geom ) const
2192   {
2193     // find the tetrahedron including the three nodes of facet
2194     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2195     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2196     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2197     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2198     while ( volIt1->more() )
2199     {
2200       const SMDS_MeshElement* v = volIt1->next();
2201       if ( v->GetGeomType() != geom )
2202         continue;
2203       const int lastCornerInd = v->NbCornerNodes() - 1;
2204       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2205         continue; // medium node not allowed
2206       const int ind2 = v->GetNodeIndex( n2 );
2207       if ( ind2 < 0 || lastCornerInd < ind2 )
2208         continue;
2209       const int ind3 = v->GetNodeIndex( n3 );
2210       if ( ind3 < 0 || lastCornerInd < ind3 )
2211         continue;
2212       return true;
2213     }
2214     return false;
2215   }
2216
2217   //=======================================================================
2218   /*!
2219    * \brief A key of a face of volume
2220    */
2221   //=======================================================================
2222
2223   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2224   {
2225     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2226     {
2227       TIDSortedNodeSet sortedNodes;
2228       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2229       int nbNodes = vol.NbFaceNodes( iF );
2230       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2231       for ( int i = 0; i < nbNodes; i += iQ )
2232         sortedNodes.insert( fNodes[i] );
2233       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2234       first.first   = (*(n++))->GetID();
2235       first.second  = (*(n++))->GetID();
2236       second.first  = (*(n++))->GetID();
2237       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2238     }
2239   };
2240 } // namespace
2241
2242 //=======================================================================
2243 //function : SplitVolumes
2244 //purpose  : Split volume elements into tetrahedra or prisms.
2245 //           If facet ID < 0, element is split into tetrahedra,
2246 //           else a hexahedron is split into prisms so that the given facet is
2247 //           split into triangles
2248 //=======================================================================
2249
2250 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2251                                      const int            theMethodFlags)
2252 {
2253   SMDS_VolumeTool    volTool;
2254   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2255   fHelper.ToFixNodeParameters( true );
2256
2257   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2258   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2259
2260   SMESH_SequenceOfElemPtr newNodes, newElems;
2261
2262   // map face of volume to it's baricenrtic node
2263   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2264   double bc[3];
2265   vector<const SMDS_MeshElement* > splitVols;
2266
2267   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2268   for ( ; elem2facet != theElems.end(); ++elem2facet )
2269   {
2270     const SMDS_MeshElement* elem = elem2facet->first;
2271     const int       facetToSplit = elem2facet->second;
2272     if ( elem->GetType() != SMDSAbs_Volume )
2273       continue;
2274     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2275     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2276       continue;
2277
2278     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2279
2280     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2281                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2282                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2283     if ( splitMethod._nbSplits < 1 ) continue;
2284
2285     // find submesh to add new tetras to
2286     if ( !subMesh || !subMesh->Contains( elem ))
2287     {
2288       int shapeID = FindShape( elem );
2289       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2290       subMesh = GetMeshDS()->MeshElements( shapeID );
2291     }
2292     int iQ;
2293     if ( elem->IsQuadratic() )
2294     {
2295       iQ = 2;
2296       // add quadratic links to the helper
2297       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2298       {
2299         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2300         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2301         for ( int iN = 0; iN < nbN; iN += iQ )
2302           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2303       }
2304       helper.SetIsQuadratic( true );
2305     }
2306     else
2307     {
2308       iQ = 1;
2309       helper.SetIsQuadratic( false );
2310     }
2311     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2312                                         volTool.GetNodes() + elem->NbNodes() );
2313     helper.SetElementsOnShape( true );
2314     if ( splitMethod._baryNode )
2315     {
2316       // make a node at barycenter
2317       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2318       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2319       nodes.push_back( gcNode );
2320       newNodes.push_back( gcNode );
2321     }
2322     if ( !splitMethod._faceBaryNode.empty() )
2323     {
2324       // make or find baricentric nodes of faces
2325       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2326       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2327       {
2328         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2329           volFace2BaryNode.insert
2330           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2331         if ( !f_n->second )
2332         {
2333           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2334           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2335         }
2336         nodes.push_back( iF_n->second = f_n->second );
2337       }
2338     }
2339
2340     // make new volumes
2341     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2342     const int* volConn = splitMethod._connectivity;
2343     if ( splitMethod._nbCorners == 4 ) // tetra
2344       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2345         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2346                                                                nodes[ volConn[1] ],
2347                                                                nodes[ volConn[2] ],
2348                                                                nodes[ volConn[3] ]));
2349     else // prisms
2350       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2351         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2352                                                                nodes[ volConn[1] ],
2353                                                                nodes[ volConn[2] ],
2354                                                                nodes[ volConn[3] ],
2355                                                                nodes[ volConn[4] ],
2356                                                                nodes[ volConn[5] ]));
2357
2358     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2359
2360     // Split faces on sides of the split volume
2361
2362     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2363     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2364     {
2365       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2366       if ( nbNodes < 4 ) continue;
2367
2368       // find an existing face
2369       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2370                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2371       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2372                                                                        /*noMedium=*/false))
2373       {
2374         // make triangles
2375         helper.SetElementsOnShape( false );
2376         vector< const SMDS_MeshElement* > triangles;
2377
2378         // find submesh to add new triangles in
2379         if ( !fSubMesh || !fSubMesh->Contains( face ))
2380         {
2381           int shapeID = FindShape( face );
2382           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2383         }
2384         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2385         if ( iF_n != splitMethod._faceBaryNode.end() )
2386         {
2387           const SMDS_MeshNode *baryNode = iF_n->second;
2388           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2389           {
2390             const SMDS_MeshNode* n1 = fNodes[iN];
2391             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2392             const SMDS_MeshNode *n3 = baryNode;
2393             if ( !volTool.IsFaceExternal( iF ))
2394               swap( n2, n3 );
2395             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2396           }
2397           if ( fSubMesh ) // update position of the bary node on geometry
2398           {
2399             if ( subMesh )
2400               subMesh->RemoveNode( baryNode );
2401             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2402             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2403             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2404             {
2405               fHelper.SetSubShape( s );
2406               gp_XY uv( 1e100, 1e100 );
2407               double distXYZ[4];
2408               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2409                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2410                    uv.X() < 1e100 )
2411               {
2412                 // node is too far from the surface
2413                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2414                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2415                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2416               }
2417             }
2418           }
2419         }
2420         else
2421         {
2422           // among possible triangles create ones described by split method
2423           const int* nInd = volTool.GetFaceNodesIndices( iF );
2424           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2425           int iCom = 0; // common node of triangle faces to split into
2426           list< TTriangleFacet > facets;
2427           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2428           {
2429             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2430                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2431                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2432             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2433                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2434                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2435             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2436             {
2437               facets.push_back( t012 );
2438               facets.push_back( t023 );
2439               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2440                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2441                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2442                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2443               break;
2444             }
2445           }
2446           list< TTriangleFacet >::iterator facet = facets.begin();
2447           if ( facet == facets.end() )
2448             break;
2449           for ( ; facet != facets.end(); ++facet )
2450           {
2451             if ( !volTool.IsFaceExternal( iF ))
2452               swap( facet->_n2, facet->_n3 );
2453             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2454                                                  volNodes[ facet->_n2 ],
2455                                                  volNodes[ facet->_n3 ]));
2456           }
2457         }
2458         for ( size_t i = 0; i < triangles.size(); ++i )
2459         {
2460           if ( !triangles[ i ]) continue;
2461           if ( fSubMesh )
2462             fSubMesh->AddElement( triangles[ i ]);
2463           newElems.push_back( triangles[ i ]);
2464         }
2465         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2466         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2467
2468       } // while a face based on facet nodes exists
2469     } // loop on volume faces to split them into triangles
2470
2471     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2472
2473     if ( geomType == SMDSEntity_TriQuad_Hexa )
2474     {
2475       // remove medium nodes that could become free
2476       for ( int i = 20; i < volTool.NbNodes(); ++i )
2477         if ( volNodes[i]->NbInverseElements() == 0 )
2478           GetMeshDS()->RemoveNode( volNodes[i] );
2479     }
2480   } // loop on volumes to split
2481
2482   myLastCreatedNodes = newNodes;
2483   myLastCreatedElems = newElems;
2484 }
2485
2486 //=======================================================================
2487 //function : GetHexaFacetsToSplit
2488 //purpose  : For hexahedra that will be split into prisms, finds facets to
2489 //           split into triangles. Only hexahedra adjacent to the one closest
2490 //           to theFacetNormal.Location() are returned.
2491 //param [in,out] theHexas - the hexahedra
2492 //param [in]     theFacetNormal - facet normal
2493 //param [out]    theFacets - the hexahedra and found facet IDs
2494 //=======================================================================
2495
2496 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2497                                              const gp_Ax1&     theFacetNormal,
2498                                              TFacetOfElem &    theFacets)
2499 {
2500 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2501
2502   // Find a hexa closest to the location of theFacetNormal
2503
2504   const SMDS_MeshElement* startHex;
2505   {
2506     // get SMDS_ElemIteratorPtr on theHexas
2507     typedef const SMDS_MeshElement*                                      TValue;
2508     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2509     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2510     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2511     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2512     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2513       ( new TElemSetIter( theHexas.begin(),
2514                           theHexas.end(),
2515                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2516
2517     SMESH_ElementSearcher* searcher =
2518       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2519
2520     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2521
2522     delete searcher;
2523
2524     if ( !startHex )
2525       throw SALOME_Exception( THIS_METHOD "startHex not found");
2526   }
2527
2528   // Select a facet of startHex by theFacetNormal
2529
2530   SMDS_VolumeTool vTool( startHex );
2531   double norm[3], dot, maxDot = 0;
2532   int facetID = -1;
2533   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2534     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2535     {
2536       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2537       if ( dot > maxDot )
2538       {
2539         facetID = iF;
2540         maxDot = dot;
2541       }
2542     }
2543   if ( facetID < 0 )
2544     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2545
2546   // Fill theFacets starting from facetID of startHex
2547
2548   // facets used for searching of volumes adjacent to already treated ones
2549   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2550   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2551   TFacetMap facetsToCheck;
2552
2553   set<const SMDS_MeshNode*> facetNodes;
2554   const SMDS_MeshElement*   curHex;
2555
2556   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2557
2558   while ( startHex )
2559   {
2560     // move in two directions from startHex via facetID
2561     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2562     {
2563       curHex       = startHex;
2564       int curFacet = facetID;
2565       if ( is2nd ) // do not treat startHex twice
2566       {
2567         vTool.Set( curHex );
2568         if ( vTool.IsFreeFace( curFacet, &curHex ))
2569         {
2570           curHex = 0;
2571         }
2572         else
2573         {
2574           vTool.GetFaceNodes( curFacet, facetNodes );
2575           vTool.Set( curHex );
2576           curFacet = vTool.GetFaceIndex( facetNodes );
2577         }
2578       }
2579       while ( curHex )
2580       {
2581         // store a facet to split
2582         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2583         {
2584           theFacets.insert( make_pair( curHex, -1 ));
2585           break;
2586         }
2587         if ( !allHex && !theHexas.count( curHex ))
2588           break;
2589
2590         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2591           theFacets.insert( make_pair( curHex, curFacet ));
2592         if ( !facetIt2isNew.second )
2593           break;
2594
2595         // remember not-to-split facets in facetsToCheck
2596         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2597         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2598         {
2599           if ( iF == curFacet && iF == oppFacet )
2600             continue;
2601           TVolumeFaceKey facetKey ( vTool, iF );
2602           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2603           pair< TFacetMap::iterator, bool > it2isnew =
2604             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2605           if ( !it2isnew.second )
2606             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2607         }
2608         // pass to a volume adjacent via oppFacet
2609         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2610         {
2611           curHex = 0;
2612         }
2613         else
2614         {
2615           // get a new curFacet
2616           vTool.GetFaceNodes( oppFacet, facetNodes );
2617           vTool.Set( curHex );
2618           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2619         }
2620       }
2621     } // move in two directions from startHex via facetID
2622
2623     // Find a new startHex by facetsToCheck
2624
2625     startHex = 0;
2626     facetID  = -1;
2627     TFacetMap::iterator fIt = facetsToCheck.begin();
2628     while ( !startHex && fIt != facetsToCheck.end() )
2629     {
2630       const TElemFacets&  elemFacets = fIt->second;
2631       const SMDS_MeshElement*    hex = elemFacets.first->first;
2632       int                 splitFacet = elemFacets.first->second;
2633       int               lateralFacet = elemFacets.second;
2634       facetsToCheck.erase( fIt );
2635       fIt = facetsToCheck.begin();
2636
2637       vTool.Set( hex );
2638       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2639            curHex->GetGeomType() != SMDSGeom_HEXA )
2640         continue;
2641       if ( !allHex && !theHexas.count( curHex ))
2642         continue;
2643
2644       startHex = curHex;
2645
2646       // find a facet of startHex to split
2647
2648       set<const SMDS_MeshNode*> lateralNodes;
2649       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2650       vTool.GetFaceNodes( splitFacet,   facetNodes );
2651       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2652       vTool.Set( startHex );
2653       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2654
2655       // look for a facet of startHex having common nodes with facetNodes
2656       // but not lateralFacet
2657       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2658       {
2659         if ( iF == lateralFacet )
2660           continue;
2661         int nbCommonNodes = 0;
2662         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2663         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2664           nbCommonNodes += facetNodes.count( nn[ iN ]);
2665
2666         if ( nbCommonNodes >= 2 )
2667         {
2668           facetID = iF;
2669           break;
2670         }
2671       }
2672       if ( facetID < 0 )
2673         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2674     }
2675   } //   while ( startHex )
2676
2677   return;
2678 }
2679
2680 namespace
2681 {
2682   //================================================================================
2683   /*!
2684    * \brief Selects nodes of several elements according to a given interlace
2685    *  \param [in] srcNodes - nodes to select from
2686    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2687    *  \param [in] interlace - indices of nodes for all elements
2688    *  \param [in] nbElems - nb of elements
2689    *  \param [in] nbNodes - nb of nodes in each element
2690    *  \param [in] mesh - the mesh
2691    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2692    *  \param [in] type - type of elements to look for
2693    */
2694   //================================================================================
2695
2696   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2697                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2698                     const int*                            interlace,
2699                     const int                             nbElems,
2700                     const int                             nbNodes,
2701                     SMESHDS_Mesh*                         mesh = 0,
2702                     list< const SMDS_MeshElement* >*      elemQueue=0,
2703                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2704   {
2705     for ( int iE = 0; iE < nbElems; ++iE )
2706     {
2707       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2708       const int*                         select = & interlace[iE*nbNodes];
2709       elemNodes.resize( nbNodes );
2710       for ( int iN = 0; iN < nbNodes; ++iN )
2711         elemNodes[iN] = srcNodes[ select[ iN ]];
2712     }
2713     const SMDS_MeshElement* e;
2714     if ( elemQueue )
2715       for ( int iE = 0; iE < nbElems; ++iE )
2716         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2717           elemQueue->push_back( e );
2718   }
2719 }
2720
2721 //=======================================================================
2722 /*
2723  * Split bi-quadratic elements into linear ones without creation of additional nodes
2724  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2725  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2726  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2727  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2728  *   will be split in order to keep the mesh conformal.
2729  *  \param elems - elements to split
2730  */
2731 //=======================================================================
2732
2733 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2734 {
2735   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2736   vector<const SMDS_MeshElement* > splitElems;
2737   list< const SMDS_MeshElement* > elemQueue;
2738   list< const SMDS_MeshElement* >::iterator elemIt;
2739
2740   SMESHDS_Mesh * mesh = GetMeshDS();
2741   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2742   int nbElems, nbNodes;
2743
2744   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2745   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2746   {
2747     elemQueue.clear();
2748     elemQueue.push_back( *elemSetIt );
2749     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2750     {
2751       const SMDS_MeshElement* elem = *elemIt;
2752       switch( elem->GetEntityType() )
2753       {
2754       case SMDSEntity_TriQuad_Hexa: // HEX27
2755       {
2756         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2757         nbElems  = nbNodes = 8;
2758         elemType = & hexaType;
2759
2760         // get nodes for new elements
2761         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2762                                  { 1,9,20,8,    17,22,26,21 },
2763                                  { 2,10,20,9,   18,23,26,22 },
2764                                  { 3,11,20,10,  19,24,26,23 },
2765                                  { 16,21,26,24, 4,12,25,15  },
2766                                  { 17,22,26,21, 5,13,25,12  },
2767                                  { 18,23,26,22, 6,14,25,13  },
2768                                  { 19,24,26,23, 7,15,25,14  }};
2769         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2770
2771         // add boundary faces to elemQueue
2772         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2773                                  { 4,5,6,7, 12,13,14,15, 25 },
2774                                  { 0,1,5,4, 8,17,12,16,  21 },
2775                                  { 1,2,6,5, 9,18,13,17,  22 },
2776                                  { 2,3,7,6, 10,19,14,18, 23 },
2777                                  { 3,0,4,7, 11,16,15,19, 24 }};
2778         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2779
2780         // add boundary segments to elemQueue
2781         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2782                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2783                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2784         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2785         break;
2786       }
2787       case SMDSEntity_BiQuad_Triangle: // TRIA7
2788       {
2789         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2790         nbElems = 3;
2791         nbNodes = 4;
2792         elemType = & quadType;
2793
2794         // get nodes for new elements
2795         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2796         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2797
2798         // add boundary segments to elemQueue
2799         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2800         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2801         break;
2802       }
2803       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2804       {
2805         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806         nbElems = 4;
2807         nbNodes = 4;
2808         elemType = & quadType;
2809
2810         // get nodes for new elements
2811         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2812         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2813
2814         // add boundary segments to elemQueue
2815         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2816         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2817         break;
2818       }
2819       case SMDSEntity_Quad_Edge:
2820       {
2821         if ( elemIt == elemQueue.begin() )
2822           continue; // an elem is in theElems
2823         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2824         nbElems = 2;
2825         nbNodes = 2;
2826         elemType = & segType;
2827
2828         // get nodes for new elements
2829         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2830         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2831         break;
2832       }
2833       default: continue;
2834       } // switch( elem->GetEntityType() )
2835
2836       // Create new elements
2837
2838       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2839
2840       splitElems.clear();
2841
2842       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2843       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2844       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2845       //elemType->SetID( -1 );
2846
2847       for ( int iE = 0; iE < nbElems; ++iE )
2848         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2849
2850
2851       ReplaceElemInGroups( elem, splitElems, mesh );
2852
2853       if ( subMesh )
2854         for ( size_t i = 0; i < splitElems.size(); ++i )
2855           subMesh->AddElement( splitElems[i] );
2856     }
2857   }
2858 }
2859
2860 //=======================================================================
2861 //function : AddToSameGroups
2862 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2863 //=======================================================================
2864
2865 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2866                                         const SMDS_MeshElement* elemInGroups,
2867                                         SMESHDS_Mesh *          aMesh)
2868 {
2869   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870   if (!groups.empty()) {
2871     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2872     for ( ; grIt != groups.end(); grIt++ ) {
2873       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2874       if ( group && group->Contains( elemInGroups ))
2875         group->SMDSGroup().Add( elemToAdd );
2876     }
2877   }
2878 }
2879
2880
2881 //=======================================================================
2882 //function : RemoveElemFromGroups
2883 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2884 //=======================================================================
2885 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2886                                              SMESHDS_Mesh *          aMesh)
2887 {
2888   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889   if (!groups.empty())
2890   {
2891     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2892     for (; GrIt != groups.end(); GrIt++)
2893     {
2894       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2895       if (!grp || grp->IsEmpty()) continue;
2896       grp->SMDSGroup().Remove(removeelem);
2897     }
2898   }
2899 }
2900
2901 //================================================================================
2902 /*!
2903  * \brief Replace elemToRm by elemToAdd in the all groups
2904  */
2905 //================================================================================
2906
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908                                             const SMDS_MeshElement* elemToAdd,
2909                                             SMESHDS_Mesh *          aMesh)
2910 {
2911   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912   if (!groups.empty()) {
2913     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914     for ( ; grIt != groups.end(); grIt++ ) {
2915       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2917         group->SMDSGroup().Add( elemToAdd );
2918     }
2919   }
2920 }
2921
2922 //================================================================================
2923 /*!
2924  * \brief Replace elemToRm by elemToAdd in the all groups
2925  */
2926 //================================================================================
2927
2928 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2929                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2930                                             SMESHDS_Mesh *                         aMesh)
2931 {
2932   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2933   if (!groups.empty())
2934   {
2935     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2936     for ( ; grIt != groups.end(); grIt++ ) {
2937       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2938       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2939         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2940           group->SMDSGroup().Add( elemToAdd[ i ] );
2941     }
2942   }
2943 }
2944
2945 //=======================================================================
2946 //function : QuadToTri
2947 //purpose  : Cut quadrangles into triangles.
2948 //           theCrit is used to select a diagonal to cut
2949 //=======================================================================
2950
2951 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2952                                   const bool         the13Diag)
2953 {
2954   ClearLastCreated();
2955   myLastCreatedElems.reserve( theElems.size() * 2 );
2956
2957   SMESHDS_Mesh *       aMesh = GetMeshDS();
2958   Handle(Geom_Surface) surface;
2959   SMESH_MesherHelper   helper( *GetMesh() );
2960
2961   TIDSortedElemSet::iterator itElem;
2962   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2963   {
2964     const SMDS_MeshElement* elem = *itElem;
2965     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2966       continue;
2967
2968     if ( elem->NbNodes() == 4 ) {
2969       // retrieve element nodes
2970       const SMDS_MeshNode* aNodes [4];
2971       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2972       int i = 0;
2973       while ( itN->more() )
2974         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2975
2976       int aShapeId = FindShape( elem );
2977       const SMDS_MeshElement* newElem1 = 0;
2978       const SMDS_MeshElement* newElem2 = 0;
2979       if ( the13Diag ) {
2980         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2981         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2982       }
2983       else {
2984         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2985         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2986       }
2987       myLastCreatedElems.push_back(newElem1);
2988       myLastCreatedElems.push_back(newElem2);
2989       // put a new triangle on the same shape and add to the same groups
2990       if ( aShapeId )
2991       {
2992         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2993         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2994       }
2995       AddToSameGroups( newElem1, elem, aMesh );
2996       AddToSameGroups( newElem2, elem, aMesh );
2997       aMesh->RemoveElement( elem );
2998     }
2999
3000     // Quadratic quadrangle
3001
3002     else if ( elem->NbNodes() >= 8 )
3003     {
3004       // get surface elem is on
3005       int aShapeId = FindShape( elem );
3006       if ( aShapeId != helper.GetSubShapeID() ) {
3007         surface.Nullify();
3008         TopoDS_Shape shape;
3009         if ( aShapeId > 0 )
3010           shape = aMesh->IndexToShape( aShapeId );
3011         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3012           TopoDS_Face face = TopoDS::Face( shape );
3013           surface = BRep_Tool::Surface( face );
3014           if ( !surface.IsNull() )
3015             helper.SetSubShape( shape );
3016         }
3017       }
3018
3019       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3020       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3021       for ( int i = 0; itN->more(); ++i )
3022         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3023
3024       const SMDS_MeshNode* centrNode = aNodes[8];
3025       if ( centrNode == 0 )
3026       {
3027         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3028                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3029                                            surface.IsNull() );
3030         myLastCreatedNodes.push_back(centrNode);
3031       }
3032
3033       // create a new element
3034       const SMDS_MeshElement* newElem1 = 0;
3035       const SMDS_MeshElement* newElem2 = 0;
3036       if ( the13Diag ) {
3037         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3038                                   aNodes[6], aNodes[7], centrNode );
3039         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040                                   centrNode, aNodes[4], aNodes[5] );
3041       }
3042       else {
3043         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044                                   aNodes[7], aNodes[4], centrNode );
3045         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046                                   centrNode, aNodes[5], aNodes[6] );
3047       }
3048       myLastCreatedElems.push_back(newElem1);
3049       myLastCreatedElems.push_back(newElem2);
3050       // put a new triangle on the same shape and add to the same groups
3051       if ( aShapeId )
3052       {
3053         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3054         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3055       }
3056       AddToSameGroups( newElem1, elem, aMesh );
3057       AddToSameGroups( newElem2, elem, aMesh );
3058       aMesh->RemoveElement( elem );
3059     }
3060   }
3061
3062   return true;
3063 }
3064
3065 //=======================================================================
3066 //function : getAngle
3067 //purpose  :
3068 //=======================================================================
3069
3070 double getAngle(const SMDS_MeshElement * tr1,
3071                 const SMDS_MeshElement * tr2,
3072                 const SMDS_MeshNode *    n1,
3073                 const SMDS_MeshNode *    n2)
3074 {
3075   double angle = 2. * M_PI; // bad angle
3076
3077   // get normals
3078   SMESH::Controls::TSequenceOfXYZ P1, P2;
3079   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3080        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3081     return angle;
3082   gp_Vec N1,N2;
3083   if(!tr1->IsQuadratic())
3084     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3085   else
3086     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3087   if ( N1.SquareMagnitude() <= gp::Resolution() )
3088     return angle;
3089   if(!tr2->IsQuadratic())
3090     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3091   else
3092     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3093   if ( N2.SquareMagnitude() <= gp::Resolution() )
3094     return angle;
3095
3096   // find the first diagonal node n1 in the triangles:
3097   // take in account a diagonal link orientation
3098   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3099   for ( int t = 0; t < 2; t++ ) {
3100     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3101     int i = 0, iDiag = -1;
3102     while ( it->more()) {
3103       const SMDS_MeshElement *n = it->next();
3104       if ( n == n1 || n == n2 ) {
3105         if ( iDiag < 0)
3106           iDiag = i;
3107         else {
3108           if ( i - iDiag == 1 )
3109             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3110           else
3111             nFirst[ t ] = n;
3112           break;
3113         }
3114       }
3115       i++;
3116     }
3117   }
3118   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3119     N2.Reverse();
3120
3121   angle = N1.Angle( N2 );
3122   //SCRUTE( angle );
3123   return angle;
3124 }
3125
3126 // =================================================
3127 // class generating a unique ID for a pair of nodes
3128 // and able to return nodes by that ID
3129 // =================================================
3130 class LinkID_Gen {
3131 public:
3132
3133   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3134     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3135   {}
3136
3137   smIdType GetLinkID (const SMDS_MeshNode * n1,
3138                   const SMDS_MeshNode * n2) const
3139   {
3140     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3141   }
3142
3143   bool GetNodes (const long             theLinkID,
3144                  const SMDS_MeshNode* & theNode1,
3145                  const SMDS_MeshNode* & theNode2) const
3146   {
3147     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3148     if ( !theNode1 ) return false;
3149     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3150     if ( !theNode2 ) return false;
3151     return true;
3152   }
3153
3154 private:
3155   LinkID_Gen();
3156   const SMESHDS_Mesh* myMesh;
3157   long                myMaxID;
3158 };
3159
3160
3161 //=======================================================================
3162 //function : TriToQuad
3163 //purpose  : Fuse neighbour triangles into quadrangles.
3164 //           theCrit is used to select a neighbour to fuse with.
3165 //           theMaxAngle is a max angle between element normals at which
3166 //           fusion is still performed.
3167 //=======================================================================
3168
3169 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3170                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3171                                   const double                         theMaxAngle)
3172 {
3173   ClearLastCreated();
3174   myLastCreatedElems.reserve( theElems.size() / 2 );
3175
3176   if ( !theCrit.get() )
3177     return false;
3178
3179   SMESHDS_Mesh * aMesh = GetMeshDS();
3180
3181   // Prepare data for algo: build
3182   // 1. map of elements with their linkIDs
3183   // 2. map of linkIDs with their elements
3184
3185   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3188   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3189
3190   TIDSortedElemSet::iterator itElem;
3191   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3192   {
3193     const SMDS_MeshElement* elem = *itElem;
3194     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195     bool IsTria = ( elem->NbCornerNodes()==3 );
3196     if (!IsTria) continue;
3197
3198     // retrieve element nodes
3199     const SMDS_MeshNode* aNodes [4];
3200     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3201     int i = 0;
3202     while ( i < 3 )
3203       aNodes[ i++ ] = itN->next();
3204     aNodes[ 3 ] = aNodes[ 0 ];
3205
3206     // fill maps
3207     for ( i = 0; i < 3; i++ ) {
3208       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209       // check if elements sharing a link can be fused
3210       itLE = mapLi_listEl.find( link );
3211       if ( itLE != mapLi_listEl.end() ) {
3212         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3213           continue;
3214         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215         //if ( FindShape( elem ) != FindShape( elem2 ))
3216         //  continue; // do not fuse triangles laying on different shapes
3217         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218           continue; // avoid making badly shaped quads
3219         (*itLE).second.push_back( elem );
3220       }
3221       else {
3222         mapLi_listEl[ link ].push_back( elem );
3223       }
3224       mapEl_setLi [ elem ].insert( link );
3225     }
3226   }
3227   // Clean the maps from the links shared by a sole element, ie
3228   // links to which only one element is bound in mapLi_listEl
3229
3230   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231     int nbElems = (*itLE).second.size();
3232     if ( nbElems < 2  ) {
3233       const SMDS_MeshElement* elem = (*itLE).second.front();
3234       SMESH_TLink link = (*itLE).first;
3235       mapEl_setLi[ elem ].erase( link );
3236       if ( mapEl_setLi[ elem ].empty() )
3237         mapEl_setLi.erase( elem );
3238     }
3239   }
3240
3241   // Algo: fuse triangles into quadrangles
3242
3243   while ( ! mapEl_setLi.empty() ) {
3244     // Look for the start element:
3245     // the element having the least nb of shared links
3246     const SMDS_MeshElement* startElem = 0;
3247     int minNbLinks = 4;
3248     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249       int nbLinks = (*itEL).second.size();
3250       if ( nbLinks < minNbLinks ) {
3251         startElem = (*itEL).first;
3252         minNbLinks = nbLinks;
3253         if ( minNbLinks == 1 )
3254           break;
3255       }
3256     }
3257
3258     // search elements to fuse starting from startElem or links of elements
3259     // fused earlyer - startLinks
3260     list< SMESH_TLink > startLinks;
3261     while ( startElem || !startLinks.empty() ) {
3262       while ( !startElem && !startLinks.empty() ) {
3263         // Get an element to start, by a link
3264         SMESH_TLink linkId = startLinks.front();
3265         startLinks.pop_front();
3266         itLE = mapLi_listEl.find( linkId );
3267         if ( itLE != mapLi_listEl.end() ) {
3268           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270           for ( ; itE != listElem.end() ; itE++ )
3271             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3272               startElem = (*itE);
3273           mapLi_listEl.erase( itLE );
3274         }
3275       }
3276
3277       if ( startElem ) {
3278         // Get candidates to be fused
3279         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280         const SMESH_TLink *link12 = 0, *link13 = 0;
3281         startElem = 0;
3282         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284         ASSERT( !setLi.empty() );
3285         set< SMESH_TLink >::iterator itLi;
3286         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3287         {
3288           const SMESH_TLink & link = (*itLi);
3289           itLE = mapLi_listEl.find( link );
3290           if ( itLE == mapLi_listEl.end() )
3291             continue;
3292
3293           const SMDS_MeshElement* elem = (*itLE).second.front();
3294           if ( elem == tr1 )
3295             elem = (*itLE).second.back();
3296           mapLi_listEl.erase( itLE );
3297           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3298             continue;
3299           if ( tr2 ) {
3300             tr3 = elem;
3301             link13 = &link;
3302           }
3303           else {
3304             tr2 = elem;
3305             link12 = &link;
3306           }
3307
3308           // add other links of elem to list of links to re-start from
3309           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310           set< SMESH_TLink >::iterator it;
3311           for ( it = links.begin(); it != links.end(); it++ ) {
3312             const SMESH_TLink& link2 = (*it);
3313             if ( link2 != link )
3314               startLinks.push_back( link2 );
3315           }
3316         }
3317
3318         // Get nodes of possible quadrangles
3319         const SMDS_MeshNode *n12 [4], *n13 [4];
3320         bool Ok12 = false, Ok13 = false;
3321         const SMDS_MeshNode *linkNode1, *linkNode2;
3322         if(tr2) {
3323           linkNode1 = link12->first;
3324           linkNode2 = link12->second;
3325           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3326             Ok12 = true;
3327         }
3328         if(tr3) {
3329           linkNode1 = link13->first;
3330           linkNode2 = link13->second;
3331           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3332             Ok13 = true;
3333         }
3334
3335         // Choose a pair to fuse
3336         if ( Ok12 && Ok13 ) {
3337           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339           double aBadRate12 = getBadRate( &quad12, theCrit );
3340           double aBadRate13 = getBadRate( &quad13, theCrit );
3341           if (  aBadRate13 < aBadRate12 )
3342             Ok12 = false;
3343           else
3344             Ok13 = false;
3345         }
3346
3347         // Make quadrangles
3348         // and remove fused elems and remove links from the maps
3349         mapEl_setLi.erase( tr1 );
3350         if ( Ok12 )
3351         {
3352           mapEl_setLi.erase( tr2 );
3353           mapLi_listEl.erase( *link12 );
3354           if ( tr1->NbNodes() == 3 )
3355           {
3356             const SMDS_MeshElement* newElem = 0;
3357             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358             myLastCreatedElems.push_back(newElem);
3359             AddToSameGroups( newElem, tr1, aMesh );
3360             int aShapeId = tr1->getshapeId();
3361             if ( aShapeId )
3362               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363             aMesh->RemoveElement( tr1 );
3364             aMesh->RemoveElement( tr2 );
3365           }
3366           else {
3367             vector< const SMDS_MeshNode* > N1;
3368             vector< const SMDS_MeshNode* > N2;
3369             getNodesFromTwoTria(tr1,tr2,N1,N2);
3370             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3372             // i.e. first nodes from both arrays form a new diagonal
3373             const SMDS_MeshNode* aNodes[8];
3374             aNodes[0] = N1[0];
3375             aNodes[1] = N1[1];
3376             aNodes[2] = N2[0];
3377             aNodes[3] = N2[1];
3378             aNodes[4] = N1[3];
3379             aNodes[5] = N2[5];
3380             aNodes[6] = N2[3];
3381             aNodes[7] = N1[5];
3382             const SMDS_MeshElement* newElem = 0;
3383             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3386             else
3387               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389             myLastCreatedElems.push_back(newElem);
3390             AddToSameGroups( newElem, tr1, aMesh );
3391             int aShapeId = tr1->getshapeId();
3392             if ( aShapeId )
3393               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394             aMesh->RemoveElement( tr1 );
3395             aMesh->RemoveElement( tr2 );
3396             // remove middle node (9)
3397             if ( N1[4]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N1[4] );
3399             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400               aMesh->RemoveNode( N1[6] );
3401             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402               aMesh->RemoveNode( N2[6] );
3403           }
3404         }
3405         else if ( Ok13 )
3406         {
3407           mapEl_setLi.erase( tr3 );
3408           mapLi_listEl.erase( *link13 );
3409           if ( tr1->NbNodes() == 3 ) {
3410             const SMDS_MeshElement* newElem = 0;
3411             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412             myLastCreatedElems.push_back(newElem);
3413             AddToSameGroups( newElem, tr1, aMesh );
3414             int aShapeId = tr1->getshapeId();
3415             if ( aShapeId )
3416               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417             aMesh->RemoveElement( tr1 );
3418             aMesh->RemoveElement( tr3 );
3419           }
3420           else {
3421             vector< const SMDS_MeshNode* > N1;
3422             vector< const SMDS_MeshNode* > N2;
3423             getNodesFromTwoTria(tr1,tr3,N1,N2);
3424             // now we receive following N1 and N2 (using numeration as above image)
3425             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3426             // i.e. first nodes from both arrays form a new diagonal
3427             const SMDS_MeshNode* aNodes[8];
3428             aNodes[0] = N1[0];
3429             aNodes[1] = N1[1];
3430             aNodes[2] = N2[0];
3431             aNodes[3] = N2[1];
3432             aNodes[4] = N1[3];
3433             aNodes[5] = N2[5];
3434             aNodes[6] = N2[3];
3435             aNodes[7] = N1[5];
3436             const SMDS_MeshElement* newElem = 0;
3437             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3440             else
3441               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443             myLastCreatedElems.push_back(newElem);
3444             AddToSameGroups( newElem, tr1, aMesh );
3445             int aShapeId = tr1->getshapeId();
3446             if ( aShapeId )
3447               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448             aMesh->RemoveElement( tr1 );
3449             aMesh->RemoveElement( tr3 );
3450             // remove middle node (9)
3451             if ( N1[4]->NbInverseElements() == 0 )
3452               aMesh->RemoveNode( N1[4] );
3453             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454               aMesh->RemoveNode( N1[6] );
3455             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456               aMesh->RemoveNode( N2[6] );
3457           }
3458         }
3459
3460         // Next element to fuse: the rejected one
3461         if ( tr3 )
3462           startElem = Ok12 ? tr3 : tr2;
3463
3464       } // if ( startElem )
3465     } // while ( startElem || !startLinks.empty() )
3466   } // while ( ! mapEl_setLi.empty() )
3467
3468   return true;
3469 }
3470
3471 //================================================================================
3472 /*!
3473  * \brief Return nodes linked to the given one
3474  * \param theNode - the node
3475  * \param linkedNodes - the found nodes
3476  * \param type - the type of elements to check
3477  *
3478  * Medium nodes are ignored
3479  */
3480 //================================================================================
3481
3482 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3483                                        TIDSortedElemSet &   linkedNodes,
3484                                        SMDSAbs_ElementType  type )
3485 {
3486   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3487   while ( elemIt->more() )
3488   {
3489     const SMDS_MeshElement* elem = elemIt->next();
3490     if(elem->GetType() == SMDSAbs_0DElement)
3491       continue;
3492
3493     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3494     if ( elem->GetType() == SMDSAbs_Volume )
3495     {
3496       SMDS_VolumeTool vol( elem );
3497       while ( nodeIt->more() ) {
3498         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3499         if ( theNode != n && vol.IsLinked( theNode, n ))
3500           linkedNodes.insert( n );
3501       }
3502     }
3503     else
3504     {
3505       for ( int i = 0; nodeIt->more(); ++i ) {
3506         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3507         if ( n == theNode ) {
3508           int iBefore = i - 1;
3509           int iAfter  = i + 1;
3510           if ( elem->IsQuadratic() ) {
3511             int nb = elem->NbNodes() / 2;
3512             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3513             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3514           }
3515           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3516           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3517         }
3518       }
3519     }
3520   }
3521 }
3522
3523 //=======================================================================
3524 //function : laplacianSmooth
3525 //purpose  : pulls theNode toward the center of surrounding nodes directly
3526 //           connected to that node along an element edge
3527 //=======================================================================
3528
3529 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3530                      const Handle(Geom_Surface)&          theSurface,
3531                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3532 {
3533   // find surrounding nodes
3534
3535   TIDSortedElemSet nodeSet;
3536   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3537
3538   // compute new coodrs
3539
3540   double coord[] = { 0., 0., 0. };
3541   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3542   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3543     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3544     if ( theSurface.IsNull() ) { // smooth in 3D
3545       coord[0] += node->X();
3546       coord[1] += node->Y();
3547       coord[2] += node->Z();
3548     }
3549     else { // smooth in 2D
3550       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3551       gp_XY* uv = theUVMap[ node ];
3552       coord[0] += uv->X();
3553       coord[1] += uv->Y();
3554     }
3555   }
3556   int nbNodes = nodeSet.size();
3557   if ( !nbNodes )
3558     return;
3559   coord[0] /= nbNodes;
3560   coord[1] /= nbNodes;
3561
3562   if ( !theSurface.IsNull() ) {
3563     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3564     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3565     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3566     coord[0] = p3d.X();
3567     coord[1] = p3d.Y();
3568     coord[2] = p3d.Z();
3569   }
3570   else
3571     coord[2] /= nbNodes;
3572
3573   // move node
3574
3575   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3576 }
3577
3578 //=======================================================================
3579 //function : centroidalSmooth
3580 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3581 //           surrounding elements
3582 //=======================================================================
3583
3584 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3585                       const Handle(Geom_Surface)&          theSurface,
3586                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3587 {
3588   gp_XYZ aNewXYZ(0.,0.,0.);
3589   SMESH::Controls::Area anAreaFunc;
3590   double totalArea = 0.;
3591   int nbElems = 0;
3592
3593   // compute new XYZ
3594
3595   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3596   while ( elemIt->more() )
3597   {
3598     const SMDS_MeshElement* elem = elemIt->next();
3599     nbElems++;
3600
3601     gp_XYZ elemCenter(0.,0.,0.);
3602     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3603     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3604     int nn = elem->NbNodes();
3605     if(elem->IsQuadratic()) nn = nn/2;
3606     int i=0;
3607     //while ( itN->more() ) {
3608     while ( i<nn ) {
3609       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3610       i++;
3611       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3612       aNodePoints.push_back( aP );
3613       if ( !theSurface.IsNull() ) { // smooth in 2D
3614         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3615         gp_XY* uv = theUVMap[ aNode ];
3616         aP.SetCoord( uv->X(), uv->Y(), 0. );
3617       }
3618       elemCenter += aP;
3619     }
3620     double elemArea = anAreaFunc.GetValue( aNodePoints );
3621     totalArea += elemArea;
3622     elemCenter /= nn;
3623     aNewXYZ += elemCenter * elemArea;
3624   }
3625   aNewXYZ /= totalArea;
3626   if ( !theSurface.IsNull() ) {
3627     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3628     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3629   }
3630
3631   // move node
3632
3633   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3634 }
3635
3636 //=======================================================================
3637 //function : getClosestUV
3638 //purpose  : return UV of closest projection
3639 //=======================================================================
3640
3641 static bool getClosestUV (Extrema_GenExtPS& projector,
3642                           const gp_Pnt&     point,
3643                           gp_XY &           result)
3644 {
3645   projector.Perform( point );
3646   if ( projector.IsDone() ) {
3647     double u = 0, v = 0, minVal = DBL_MAX;
3648     for ( int i = projector.NbExt(); i > 0; i-- )
3649       if ( projector.SquareDistance( i ) < minVal ) {
3650         minVal = projector.SquareDistance( i );
3651         projector.Point( i ).Parameter( u, v );
3652       }
3653     result.SetCoord( u, v );
3654     return true;
3655   }
3656   return false;
3657 }
3658
3659 //=======================================================================
3660 //function : Smooth
3661 //purpose  : Smooth theElements during theNbIterations or until a worst
3662 //           element has aspect ratio <= theTgtAspectRatio.
3663 //           Aspect Ratio varies in range [1.0, inf].
3664 //           If theElements is empty, the whole mesh is smoothed.
3665 //           theFixedNodes contains additionally fixed nodes. Nodes built
3666 //           on edges and boundary nodes are always fixed.
3667 //=======================================================================
3668
3669 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3670                                set<const SMDS_MeshNode*> & theFixedNodes,
3671                                const SmoothMethod          theSmoothMethod,
3672                                const int                   theNbIterations,
3673                                double                      theTgtAspectRatio,
3674                                const bool                  the2D)
3675 {
3676   ClearLastCreated();
3677
3678   if ( theTgtAspectRatio < 1.0 )
3679     theTgtAspectRatio = 1.0;
3680
3681   const double disttol = 1.e-16;
3682
3683   SMESH::Controls::AspectRatio aQualityFunc;
3684
3685   SMESHDS_Mesh* aMesh = GetMeshDS();
3686
3687   if ( theElems.empty() ) {
3688     // add all faces to theElems
3689     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3690     while ( fIt->more() ) {
3691       const SMDS_MeshElement* face = fIt->next();
3692       theElems.insert( theElems.end(), face );
3693     }
3694   }
3695   // get all face ids theElems are on
3696   set< int > faceIdSet;
3697   TIDSortedElemSet::iterator itElem;
3698   if ( the2D )
3699     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3700       int fId = FindShape( *itElem );
3701       // check that corresponding submesh exists and a shape is face
3702       if (fId &&
3703           faceIdSet.find( fId ) == faceIdSet.end() &&
3704           aMesh->MeshElements( fId )) {
3705         TopoDS_Shape F = aMesh->IndexToShape( fId );
3706         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3707           faceIdSet.insert( fId );
3708       }
3709     }
3710   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3711
3712   // ===============================================
3713   // smooth elements on each TopoDS_Face separately
3714   // ===============================================
3715
3716   SMESH_MesherHelper helper( *GetMesh() );
3717
3718   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3719   for ( ; fId != faceIdSet.rend(); ++fId )
3720   {
3721     // get face surface and submesh
3722     Handle(Geom_Surface) surface;
3723     SMESHDS_SubMesh* faceSubMesh = 0;
3724     TopoDS_Face face;
3725     double fToler2 = 0;
3726     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3727     bool isUPeriodic = false, isVPeriodic = false;
3728     if ( *fId )
3729     {
3730       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3731       surface = BRep_Tool::Surface( face );
3732       faceSubMesh = aMesh->MeshElements( *fId );
3733       fToler2 = BRep_Tool::Tolerance( face );
3734       fToler2 *= fToler2 * 10.;
3735       isUPeriodic = surface->IsUPeriodic();
3736       // if ( isUPeriodic )
3737       //   surface->UPeriod();
3738       isVPeriodic = surface->IsVPeriodic();
3739       // if ( isVPeriodic )
3740       //   surface->VPeriod();
3741       surface->Bounds( u1, u2, v1, v2 );
3742       helper.SetSubShape( face );
3743     }
3744     // ---------------------------------------------------------
3745     // for elements on a face, find movable and fixed nodes and
3746     // compute UV for them
3747     // ---------------------------------------------------------
3748     bool checkBoundaryNodes = false;
3749     bool isQuadratic = false;
3750     set<const SMDS_MeshNode*> setMovableNodes;
3751     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3752     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3753     list< const SMDS_MeshElement* > elemsOnFace;
3754
3755     Extrema_GenExtPS projector;
3756     GeomAdaptor_Surface surfAdaptor;
3757     if ( !surface.IsNull() ) {
3758       surfAdaptor.Load( surface );
3759       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3760     }
3761     int nbElemOnFace = 0;
3762     itElem = theElems.begin();
3763     // loop on not yet smoothed elements: look for elems on a face
3764     while ( itElem != theElems.end() )
3765     {
3766       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3767         break; // all elements found
3768
3769       const SMDS_MeshElement* elem = *itElem;
3770       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3771            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3772         ++itElem;
3773         continue;
3774       }
3775       elemsOnFace.push_back( elem );
3776       theElems.erase( itElem++ );
3777       nbElemOnFace++;
3778
3779       if ( !isQuadratic )
3780         isQuadratic = elem->IsQuadratic();
3781
3782       // get movable nodes of elem
3783       const SMDS_MeshNode* node;
3784       SMDS_TypeOfPosition posType;
3785       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3786       int nn = 0, nbn =  elem->NbNodes();
3787       if(elem->IsQuadratic())
3788         nbn = nbn/2;
3789       while ( nn++ < nbn ) {
3790         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3791         const SMDS_PositionPtr& pos = node->GetPosition();
3792         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3793         if (posType != SMDS_TOP_EDGE &&
3794             posType != SMDS_TOP_VERTEX &&
3795             theFixedNodes.find( node ) == theFixedNodes.end())
3796         {
3797           // check if all faces around the node are on faceSubMesh
3798           // because a node on edge may be bound to face
3799           bool all = true;
3800           if ( faceSubMesh ) {
3801             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802             while ( eIt->more() && all ) {
3803               const SMDS_MeshElement* e = eIt->next();
3804               all = faceSubMesh->Contains( e );
3805             }
3806           }
3807           if ( all )
3808             setMovableNodes.insert( node );
3809           else
3810             checkBoundaryNodes = true;
3811         }
3812         if ( posType == SMDS_TOP_3DSPACE )
3813           checkBoundaryNodes = true;
3814       }
3815
3816       if ( surface.IsNull() )
3817         continue;
3818
3819       // get nodes to check UV
3820       list< const SMDS_MeshNode* > uvCheckNodes;
3821       const SMDS_MeshNode* nodeInFace = 0;
3822       itN = elem->nodesIterator();
3823       nn = 0; nbn =  elem->NbNodes();
3824       if(elem->IsQuadratic())
3825         nbn = nbn/2;
3826       while ( nn++ < nbn ) {
3827         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3828         if ( node->GetPosition()->GetDim() == 2 )
3829           nodeInFace = node;
3830         if ( uvMap.find( node ) == uvMap.end() )
3831           uvCheckNodes.push_back( node );
3832         // add nodes of elems sharing node
3833         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3834         //         while ( eIt->more() ) {
3835         //           const SMDS_MeshElement* e = eIt->next();
3836         //           if ( e != elem ) {
3837         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3838         //             while ( nIt->more() ) {
3839         //               const SMDS_MeshNode* n =
3840         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3841         //               if ( uvMap.find( n ) == uvMap.end() )
3842         //                 uvCheckNodes.push_back( n );
3843         //             }
3844         //           }
3845         //         }
3846       }
3847       // check UV on face
3848       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3849       for ( ; n != uvCheckNodes.end(); ++n ) {
3850         node = *n;
3851         gp_XY uv( 0, 0 );
3852         const SMDS_PositionPtr& pos = node->GetPosition();
3853         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3854         // get existing UV
3855         if ( pos )
3856         {
3857           bool toCheck = true;
3858           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3859         }
3860         // compute not existing UV
3861         bool project = ( posType == SMDS_TOP_3DSPACE );
3862         // double dist1 = DBL_MAX, dist2 = 0;
3863         // if ( posType != SMDS_TOP_3DSPACE ) {
3864         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3865         //   project = dist1 > fToler2;
3866         // }
3867         if ( project ) { // compute new UV
3868           gp_XY newUV;
3869           gp_Pnt pNode = SMESH_NodeXYZ( node );
3870           if ( !getClosestUV( projector, pNode, newUV )) {
3871             MESSAGE("Node Projection Failed " << node);
3872           }
3873           else {
3874             if ( isUPeriodic )
3875               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3876             if ( isVPeriodic )
3877               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3878             // check new UV
3879             // if ( posType != SMDS_TOP_3DSPACE )
3880             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3881             // if ( dist2 < dist1 )
3882             uv = newUV;
3883           }
3884         }
3885         // store UV in the map
3886         listUV.push_back( uv );
3887         uvMap.insert( make_pair( node, &listUV.back() ));
3888       }
3889     } // loop on not yet smoothed elements
3890
3891     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3892       checkBoundaryNodes = true;
3893
3894     // fix nodes on mesh boundary
3895
3896     if ( checkBoundaryNodes ) {
3897       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3898       map< SMESH_TLink, int >::iterator link_nb;
3899       // put all elements links to linkNbMap
3900       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3901       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3902         const SMDS_MeshElement* elem = (*elemIt);
3903         int nbn =  elem->NbCornerNodes();
3904         // loop on elem links: insert them in linkNbMap
3905         for ( int iN = 0; iN < nbn; ++iN ) {
3906           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3907           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3908           SMESH_TLink link( n1, n2 );
3909           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3910           link_nb->second++;
3911         }
3912       }
3913       // remove nodes that are in links encountered only once from setMovableNodes
3914       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3915         if ( link_nb->second == 1 ) {
3916           setMovableNodes.erase( link_nb->first.node1() );
3917           setMovableNodes.erase( link_nb->first.node2() );
3918         }
3919       }
3920     }
3921
3922     // -----------------------------------------------------
3923     // for nodes on seam edge, compute one more UV ( uvMap2 );
3924     // find movable nodes linked to nodes on seam and which
3925     // are to be smoothed using the second UV ( uvMap2 )
3926     // -----------------------------------------------------
3927
3928     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3929     if ( !surface.IsNull() ) {
3930       TopExp_Explorer eExp( face, TopAbs_EDGE );
3931       for ( ; eExp.More(); eExp.Next() ) {
3932         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3933         if ( !BRep_Tool::IsClosed( edge, face ))
3934           continue;
3935         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3936         if ( !sm ) continue;
3937         // find out which parameter varies for a node on seam
3938         double f,l;
3939         gp_Pnt2d uv1, uv2;
3940         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941         if ( pcurve.IsNull() ) continue;
3942         uv1 = pcurve->Value( f );
3943         edge.Reverse();
3944         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3945         if ( pcurve.IsNull() ) continue;
3946         uv2 = pcurve->Value( f );
3947         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3948         // assure uv1 < uv2
3949         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3950           std::swap( uv1, uv2 );
3951         // get nodes on seam and its vertices
3952         list< const SMDS_MeshNode* > seamNodes;
3953         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3954         while ( nSeamIt->more() ) {
3955           const SMDS_MeshNode* node = nSeamIt->next();
3956           if ( !isQuadratic || !IsMedium( node ))
3957             seamNodes.push_back( node );
3958         }
3959         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3960         for ( ; vExp.More(); vExp.Next() ) {
3961           sm = aMesh->MeshElements( vExp.Current() );
3962           if ( sm ) {
3963             nSeamIt = sm->GetNodes();
3964             while ( nSeamIt->more() )
3965               seamNodes.push_back( nSeamIt->next() );
3966           }
3967         }
3968         // loop on nodes on seam
3969         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3970         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3971           const SMDS_MeshNode* nSeam = *noSeIt;
3972           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3973           if ( n_uv == uvMap.end() )
3974             continue;
3975           // set the first UV
3976           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3977           // set the second UV
3978           listUV.push_back( *n_uv->second );
3979           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3980           if ( uvMap2.empty() )
3981             uvMap2 = uvMap; // copy the uvMap contents
3982           uvMap2[ nSeam ] = &listUV.back();
3983
3984           // collect movable nodes linked to ones on seam in nodesNearSeam
3985           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3986           while ( eIt->more() ) {
3987             const SMDS_MeshElement* e = eIt->next();
3988             int nbUseMap1 = 0, nbUseMap2 = 0;
3989             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3990             int nn = 0, nbn =  e->NbNodes();
3991             if(e->IsQuadratic()) nbn = nbn/2;
3992             while ( nn++ < nbn )
3993             {
3994               const SMDS_MeshNode* n =
3995                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3996               if (n == nSeam ||
3997                   setMovableNodes.find( n ) == setMovableNodes.end() )
3998                 continue;
3999               // add only nodes being closer to uv2 than to uv1
4000               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4001               //              0.5 * ( n->Y() + nSeam->Y() ),
4002               //              0.5 * ( n->Z() + nSeam->Z() ));
4003               // gp_XY uv;
4004               // getClosestUV( projector, pMid, uv );
4005               double x = uvMap[ n ]->Coord( iPar );
4006               if ( Abs( uv1.Coord( iPar ) - x ) >
4007                    Abs( uv2.Coord( iPar ) - x )) {
4008                 nodesNearSeam.insert( n );
4009                 nbUseMap2++;
4010               }
4011               else
4012                 nbUseMap1++;
4013             }
4014             // for centroidalSmooth all element nodes must
4015             // be on one side of a seam
4016             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4017               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4018               nn = 0;
4019               while ( nn++ < nbn ) {
4020                 const SMDS_MeshNode* n =
4021                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4022                 setMovableNodes.erase( n );
4023               }
4024             }
4025           }
4026         } // loop on nodes on seam
4027       } // loop on edge of a face
4028     } // if ( !face.IsNull() )
4029
4030     if ( setMovableNodes.empty() ) {
4031       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4032       continue; // goto next face
4033     }
4034
4035     // -------------
4036     // SMOOTHING //
4037     // -------------
4038
4039     int it = -1;
4040     double maxRatio = -1., maxDisplacement = -1.;
4041     set<const SMDS_MeshNode*>::iterator nodeToMove;
4042     for ( it = 0; it < theNbIterations; it++ ) {
4043       maxDisplacement = 0.;
4044       nodeToMove = setMovableNodes.begin();
4045       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4046         const SMDS_MeshNode* node = (*nodeToMove);
4047         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4048
4049         // smooth
4050         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4051         if ( theSmoothMethod == LAPLACIAN )
4052           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4053         else
4054           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4055
4056         // node displacement
4057         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4058         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4059         if ( aDispl > maxDisplacement )
4060           maxDisplacement = aDispl;
4061       }
4062       // no node movement => exit
4063       //if ( maxDisplacement < 1.e-16 ) {
4064       if ( maxDisplacement < disttol ) {
4065         MESSAGE("-- no node movement --");
4066         break;
4067       }
4068
4069       // check elements quality
4070       maxRatio  = 0;
4071       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4072       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4073         const SMDS_MeshElement* elem = (*elemIt);
4074         if ( !elem || elem->GetType() != SMDSAbs_Face )
4075           continue;
4076         SMESH::Controls::TSequenceOfXYZ aPoints;
4077         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4078           double aValue = aQualityFunc.GetValue( aPoints );
4079           if ( aValue > maxRatio )
4080             maxRatio = aValue;
4081         }
4082       }
4083       if ( maxRatio <= theTgtAspectRatio ) {
4084         //MESSAGE("-- quality achieved --");
4085         break;
4086       }
4087       if (it+1 == theNbIterations) {
4088         //MESSAGE("-- Iteration limit exceeded --");
4089       }
4090     } // smoothing iterations
4091
4092     // MESSAGE(" Face id: " << *fId <<
4093     //         " Nb iterstions: " << it <<
4094     //         " Displacement: " << maxDisplacement <<
4095     //         " Aspect Ratio " << maxRatio);
4096
4097     // ---------------------------------------
4098     // new nodes positions are computed,
4099     // record movement in DS and set new UV
4100     // ---------------------------------------
4101     nodeToMove = setMovableNodes.begin();
4102     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4103       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4104       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4105       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4106       if ( node_uv != uvMap.end() ) {
4107         gp_XY* uv = node_uv->second;
4108         node->SetPosition
4109           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4110       }
4111     }
4112
4113     // move medium nodes of quadratic elements
4114     if ( isQuadratic )
4115     {
4116       vector<const SMDS_MeshNode*> nodes;
4117       bool checkUV;
4118       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4119       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4120       {
4121         const SMDS_MeshElement* QF = *elemIt;
4122         if ( QF->IsQuadratic() )
4123         {
4124           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4125                         SMDS_MeshElement::iterator() );
4126           nodes.push_back( nodes[0] );
4127           gp_Pnt xyz;
4128           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4129           {
4130             if ( !surface.IsNull() )
4131             {
4132               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4133               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4134               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4135               xyz = surface->Value( uv.X(), uv.Y() );
4136             }
4137             else {
4138               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4139             }
4140             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4141               // we have to move a medium node
4142               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4143           }
4144         }
4145       }
4146     }
4147
4148   } // loop on face ids
4149
4150 }
4151
4152 namespace
4153 {
4154   //=======================================================================
4155   //function : isReverse
4156   //purpose  : Return true if normal of prevNodes is not co-directied with
4157   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4158   //           iNotSame is where prevNodes and nextNodes are different.
4159   //           If result is true then future volume orientation is OK
4160   //=======================================================================
4161
4162   bool isReverse(const SMDS_MeshElement*             face,
4163                  const vector<const SMDS_MeshNode*>& prevNodes,
4164                  const vector<const SMDS_MeshNode*>& nextNodes,
4165                  const int                           iNotSame)
4166   {
4167
4168     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4169     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4170     gp_XYZ extrDir( pN - pP ), faceNorm;
4171     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4172
4173     return faceNorm * extrDir < 0.0;
4174   }
4175
4176   //================================================================================
4177   /*!
4178    * \brief Assure that theElemSets[0] holds elements, not nodes
4179    */
4180   //================================================================================
4181
4182   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4183   {
4184     if ( !theElemSets[0].empty() &&
4185          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4186     {
4187       std::swap( theElemSets[0], theElemSets[1] );
4188     }
4189     else if ( !theElemSets[1].empty() &&
4190               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4191     {
4192       std::swap( theElemSets[0], theElemSets[1] );
4193     }
4194   }
4195 }
4196
4197 //=======================================================================
4198 /*!
4199  * \brief Create elements by sweeping an element
4200  * \param elem - element to sweep
4201  * \param newNodesItVec - nodes generated from each node of the element
4202  * \param newElems - generated elements
4203  * \param nbSteps - number of sweeping steps
4204  * \param srcElements - to append elem for each generated element
4205  */
4206 //=======================================================================
4207
4208 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4209                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4210                                     list<const SMDS_MeshElement*>&        newElems,
4211                                     const size_t                          nbSteps,
4212                                     SMESH_SequenceOfElemPtr&              srcElements)
4213 {
4214   SMESHDS_Mesh* aMesh = GetMeshDS();
4215
4216   const int           nbNodes = elem->NbNodes();
4217   const int         nbCorners = elem->NbCornerNodes();
4218   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4219                                                           polyhedron creation !!! */
4220   // Loop on elem nodes:
4221   // find new nodes and detect same nodes indices
4222   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4223   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4224   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4225   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4226
4227   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4228   vector<int> sames(nbNodes);
4229   vector<bool> isSingleNode(nbNodes);
4230
4231   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4232     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4233     const SMDS_MeshNode*                         node = nnIt->first;
4234     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4235     if ( listNewNodes.empty() )
4236       return;
4237
4238     itNN   [ iNode ] = listNewNodes.begin();
4239     prevNod[ iNode ] = node;
4240     nextNod[ iNode ] = listNewNodes.front();
4241
4242     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4243                                                              corner node of linear */
4244     if ( prevNod[ iNode ] != nextNod [ iNode ])
4245       nbDouble += !isSingleNode[iNode];
4246
4247     if( iNode < nbCorners ) { // check corners only
4248       if ( prevNod[ iNode ] == nextNod [ iNode ])
4249         sames[nbSame++] = iNode;
4250       else
4251         iNotSameNode = iNode;
4252     }
4253   }
4254
4255   if ( nbSame == nbNodes || nbSame > 2) {
4256     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4257     return;
4258   }
4259
4260   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4261   {
4262     // fix nodes order to have bottom normal external
4263     if ( baseType == SMDSEntity_Polygon )
4264     {
4265       std::reverse( itNN.begin(), itNN.end() );
4266       std::reverse( prevNod.begin(), prevNod.end() );
4267       std::reverse( midlNod.begin(), midlNod.end() );
4268       std::reverse( nextNod.begin(), nextNod.end() );
4269       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4270     }
4271     else
4272     {
4273       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4274       SMDS_MeshCell::applyInterlace( ind, itNN );
4275       SMDS_MeshCell::applyInterlace( ind, prevNod );
4276       SMDS_MeshCell::applyInterlace( ind, nextNod );
4277       SMDS_MeshCell::applyInterlace( ind, midlNod );
4278       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4279       if ( nbSame > 0 )
4280       {
4281         sames[nbSame] = iNotSameNode;
4282         for ( int j = 0; j <= nbSame; ++j )
4283           for ( size_t i = 0; i < ind.size(); ++i )
4284             if ( ind[i] == sames[j] )
4285             {
4286               sames[j] = i;
4287               break;
4288             }
4289         iNotSameNode = sames[nbSame];
4290       }
4291     }
4292   }
4293   else if ( elem->GetType() == SMDSAbs_Edge )
4294   {
4295     // orient a new face same as adjacent one
4296     int i1, i2;
4297     const SMDS_MeshElement* e;
4298     TIDSortedElemSet dummy;
4299     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4300         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4301         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4302     {
4303       // there is an adjacent face, check order of nodes in it
4304       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4305       if ( sameOrder )
4306       {
4307         std::swap( itNN[0],    itNN[1] );
4308         std::swap( prevNod[0], prevNod[1] );
4309         std::swap( nextNod[0], nextNod[1] );
4310         std::swap( isSingleNode[0], isSingleNode[1] );
4311         if ( nbSame > 0 )
4312           sames[0] = 1 - sames[0];
4313         iNotSameNode = 1 - iNotSameNode;
4314       }
4315     }
4316   }
4317
4318   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4319   if ( nbSame > 0 ) {
4320     iSameNode    = sames[ nbSame-1 ];
4321     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4322     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4323     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4324   }
4325
4326   if ( baseType == SMDSEntity_Polygon )
4327   {
4328     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4329     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4330   }
4331   else if ( baseType == SMDSEntity_Quad_Polygon )
4332   {
4333     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4334     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4335   }
4336
4337   // make new elements
4338   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4339   {
4340     // get next nodes
4341     for ( iNode = 0; iNode < nbNodes; iNode++ )
4342     {
4343       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4344       nextNod[ iNode ] = *itNN[ iNode ]++;
4345     }
4346
4347     SMDS_MeshElement* aNewElem = 0;
4348     /*if(!elem->IsPoly())*/ {
4349       switch ( baseType ) {
4350       case SMDSEntity_0D:
4351       case SMDSEntity_Node: { // sweep NODE
4352         if ( nbSame == 0 ) {
4353           if ( isSingleNode[0] )
4354             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4355           else
4356             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4357         }
4358         else
4359           return;
4360         break;
4361       }
4362       case SMDSEntity_Edge: { // sweep EDGE
4363         if ( nbDouble == 0 )
4364         {
4365           if ( nbSame == 0 ) // ---> quadrangle
4366             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4367                                       nextNod[ 1 ], nextNod[ 0 ] );
4368           else               // ---> triangle
4369             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4370                                       nextNod[ iNotSameNode ] );
4371         }
4372         else                 // ---> polygon
4373         {
4374           vector<const SMDS_MeshNode*> poly_nodes;
4375           poly_nodes.push_back( prevNod[0] );
4376           poly_nodes.push_back( prevNod[1] );
4377           if ( prevNod[1] != nextNod[1] )
4378           {
4379             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4380             poly_nodes.push_back( nextNod[1] );
4381           }
4382           if ( prevNod[0] != nextNod[0] )
4383           {
4384             poly_nodes.push_back( nextNod[0] );
4385             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4386           }
4387           switch ( poly_nodes.size() ) {
4388           case 3:
4389             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4390             break;
4391           case 4:
4392             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4393                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4394             break;
4395           default:
4396             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4397           }
4398         }
4399         break;
4400       }
4401       case SMDSEntity_Triangle: // TRIANGLE --->
4402       {
4403         if ( nbDouble > 0 ) break;
4404         if ( nbSame == 0 )       // ---> pentahedron
4405           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4406                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4407
4408         else if ( nbSame == 1 )  // ---> pyramid
4409           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4410                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4411                                        nextNod[ iSameNode ]);
4412
4413         else // 2 same nodes:       ---> tetrahedron
4414           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4415                                        nextNod[ iNotSameNode ]);
4416         break;
4417       }
4418       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4419       {
4420         if ( nbSame == 2 )
4421           return;
4422         if ( nbDouble+nbSame == 2 )
4423         {
4424           if(nbSame==0) {      // ---> quadratic quadrangle
4425             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4426                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4427           }
4428           else { //(nbSame==1) // ---> quadratic triangle
4429             if(sames[0]==2) {
4430               return; // medium node on axis
4431             }
4432             else if(sames[0]==0)
4433               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4434                                         prevNod[2], midlNod[1], nextNod[2] );
4435             else // sames[0]==1
4436               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4437                                         prevNod[2], nextNod[2], midlNod[0]);
4438           }
4439         }
4440         else if ( nbDouble == 3 )
4441         {
4442           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4443             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4444                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4445           }
4446         }
4447         else
4448           return;
4449         break;
4450       }
4451       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4452         if ( nbDouble > 0 ) break;
4453
4454         if ( nbSame == 0 )       // ---> hexahedron
4455           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4456                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4457
4458         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4459           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4460                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4461                                        nextNod[ iSameNode ]);
4462           newElems.push_back( aNewElem );
4463           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4464                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4465                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4466         }
4467         else if ( nbSame == 2 ) { // ---> pentahedron
4468           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4469             // iBeforeSame is same too
4470             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4471                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4472                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4473           else
4474             // iAfterSame is same too
4475             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4476                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4477                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4478         }
4479         break;
4480       }
4481       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4482       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4483         if ( nbDouble+nbSame != 3 ) break;
4484         if(nbSame==0) {
4485           // --->  pentahedron with 15 nodes
4486           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4487                                        nextNod[0], nextNod[1], nextNod[2],
4488                                        prevNod[3], prevNod[4], prevNod[5],
4489                                        nextNod[3], nextNod[4], nextNod[5],
4490                                        midlNod[0], midlNod[1], midlNod[2]);
4491         }
4492         else if(nbSame==1) {
4493           // --->  2d order pyramid of 13 nodes
4494           int apex = iSameNode;
4495           int i0 = ( apex + 1 ) % nbCorners;
4496           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4497           int i0a = apex + 3;
4498           int i1a = i1 + 3;
4499           int i01 = i0 + 3;
4500           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4501                                       nextNod[i0], nextNod[i1], prevNod[apex],
4502                                       prevNod[i01], midlNod[i0],
4503                                       nextNod[i01], midlNod[i1],
4504                                       prevNod[i1a], prevNod[i0a],
4505                                       nextNod[i0a], nextNod[i1a]);
4506         }
4507         else if(nbSame==2) {
4508           // --->  2d order tetrahedron of 10 nodes
4509           int n1 = iNotSameNode;
4510           int n2 = ( n1 + 1             ) % nbCorners;
4511           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4512           int n12 = n1 + 3;
4513           int n23 = n2 + 3;
4514           int n31 = n3 + 3;
4515           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4516                                        prevNod[n12], prevNod[n23], prevNod[n31],
4517                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4522         if( nbSame == 0 ) {
4523           if ( nbDouble != 4 ) break;
4524           // --->  hexahedron with 20 nodes
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4526                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4527                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4528                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4529                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4530         }
4531         else if(nbSame==1) {
4532           // ---> pyramid + pentahedron - can not be created since it is needed
4533           // additional middle node at the center of face
4534           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4535           return;
4536         }
4537         else if( nbSame == 2 ) {
4538           if ( nbDouble != 2 ) break;
4539           // --->  2d order Pentahedron with 15 nodes
4540           int n1,n2,n4,n5;
4541           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4542             // iBeforeSame is same too
4543             n1 = iBeforeSame;
4544             n2 = iOpposSame;
4545             n4 = iSameNode;
4546             n5 = iAfterSame;
4547           }
4548           else {
4549             // iAfterSame is same too
4550             n1 = iSameNode;
4551             n2 = iBeforeSame;
4552             n4 = iAfterSame;
4553             n5 = iOpposSame;
4554           }
4555           int n12 = n2 + 4;
4556           int n45 = n4 + 4;
4557           int n14 = n1 + 4;
4558           int n25 = n5 + 4;
4559           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4560                                        prevNod[n4], prevNod[n5], nextNod[n5],
4561                                        prevNod[n12], midlNod[n2], nextNod[n12],
4562                                        prevNod[n45], midlNod[n5], nextNod[n45],
4563                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4564         }
4565         break;
4566       }
4567       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4568
4569         if( nbSame == 0 && nbDouble == 9 ) {
4570           // --->  tri-quadratic hexahedron with 27 nodes
4571           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4572                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4573                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4574                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4575                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4576                                        prevNod[8], // bottom center
4577                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4578                                        nextNod[8], // top center
4579                                        midlNod[8]);// elem center
4580         }
4581         else
4582         {
4583           return;
4584         }
4585         break;
4586       }
4587       case SMDSEntity_Polygon: { // sweep POLYGON
4588
4589         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4590           // --->  hexagonal prism
4591           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4592                                        prevNod[3], prevNod[4], prevNod[5],
4593                                        nextNod[0], nextNod[1], nextNod[2],
4594                                        nextNod[3], nextNod[4], nextNod[5]);
4595         }
4596         break;
4597       }
4598       case SMDSEntity_Ball:
4599         return;
4600
4601       default:
4602         break;
4603       } // switch ( baseType )
4604     } // scope
4605
4606     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4607     {
4608       if ( baseType != SMDSEntity_Polygon )
4609       {
4610         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4611         SMDS_MeshCell::applyInterlace( ind, prevNod );
4612         SMDS_MeshCell::applyInterlace( ind, nextNod );
4613         SMDS_MeshCell::applyInterlace( ind, midlNod );
4614         SMDS_MeshCell::applyInterlace( ind, itNN );
4615         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4616         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4617       }
4618       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4619       vector<int> quantities (nbNodes + 2);
4620       polyedre_nodes.clear();
4621       quantities.clear();
4622
4623       // bottom of prism
4624       for (int inode = 0; inode < nbNodes; inode++)
4625         polyedre_nodes.push_back( prevNod[inode] );
4626       quantities.push_back( nbNodes );
4627
4628       // top of prism
4629       polyedre_nodes.push_back( nextNod[0] );
4630       for (int inode = nbNodes; inode-1; --inode )
4631         polyedre_nodes.push_back( nextNod[inode-1] );
4632       quantities.push_back( nbNodes );
4633
4634       // side faces
4635       // 3--6--2
4636       // |     |
4637       // 7     5
4638       // |     |
4639       // 0--4--1
4640       const int iQuad = elem->IsQuadratic();
4641       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4642       {
4643         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4644         int inextface = (iface+1+iQuad) % nbNodes;
4645         int imid      = (iface+1) % nbNodes;
4646         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4647         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4648         polyedre_nodes.push_back( prevNod[iface] );             // 1
4649         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4650         {
4651           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4652           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4653         }
4654         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4655         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4656         {
4657           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4658           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4659         }
4660         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4661         if ( nbFaceNodes > 2 )
4662           quantities.push_back( nbFaceNodes );
4663         else // degenerated face
4664           polyedre_nodes.resize( prevNbNodes );
4665       }
4666       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4667
4668     } // try to create a polyherdal prism
4669
4670     if ( aNewElem ) {
4671       newElems.push_back( aNewElem );
4672       myLastCreatedElems.push_back(aNewElem);
4673       srcElements.push_back( elem );
4674     }
4675
4676     // set new prev nodes
4677     for ( iNode = 0; iNode < nbNodes; iNode++ )
4678       prevNod[ iNode ] = nextNod[ iNode ];
4679
4680   } // loop on steps
4681 }
4682
4683 //=======================================================================
4684 /*!
4685  * \brief Create 1D and 2D elements around swept elements
4686  * \param mapNewNodes - source nodes and ones generated from them
4687  * \param newElemsMap - source elements and ones generated from them
4688  * \param elemNewNodesMap - nodes generated from each node of each element
4689  * \param elemSet - all swept elements
4690  * \param nbSteps - number of sweeping steps
4691  * \param srcElements - to append elem for each generated element
4692  */
4693 //=======================================================================
4694
4695 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4696                                   TTElemOfElemListMap &    newElemsMap,
4697                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4698                                   TIDSortedElemSet&        elemSet,
4699                                   const int                nbSteps,
4700                                   SMESH_SequenceOfElemPtr& srcElements)
4701 {
4702   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4703   SMESHDS_Mesh* aMesh = GetMeshDS();
4704
4705   // Find nodes belonging to only one initial element - sweep them into edges.
4706
4707   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4708   for ( ; nList != mapNewNodes.end(); nList++ )
4709   {
4710     const SMDS_MeshNode* node =
4711       static_cast<const SMDS_MeshNode*>( nList->first );
4712     if ( newElemsMap.count( node ))
4713       continue; // node was extruded into edge
4714     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4715     int nbInitElems = 0;
4716     const SMDS_MeshElement* el = 0;
4717     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4718     while ( eIt->more() && nbInitElems < 2 ) {
4719       const SMDS_MeshElement* e = eIt->next();
4720       SMDSAbs_ElementType  type = e->GetType();
4721       if ( type == SMDSAbs_Volume ||
4722            type < highType ||
4723            !elemSet.count(e))
4724         continue;
4725       if ( type > highType ) {
4726         nbInitElems = 0;
4727         highType    = type;
4728       }
4729       el = e;
4730       ++nbInitElems;
4731     }
4732     if ( nbInitElems == 1 ) {
4733       bool NotCreateEdge = el && el->IsMediumNode(node);
4734       if(!NotCreateEdge) {
4735         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4736         list<const SMDS_MeshElement*> newEdges;
4737         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4738       }
4739     }
4740   }
4741
4742   // Make a ceiling for each element ie an equal element of last new nodes.
4743   // Find free links of faces - make edges and sweep them into faces.
4744
4745   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4746
4747   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4748   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4749   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4750   {
4751     const SMDS_MeshElement* elem = itElem->first;
4752     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4753
4754     if(itElem->second.size()==0) continue;
4755
4756     const bool isQuadratic = elem->IsQuadratic();
4757
4758     if ( elem->GetType() == SMDSAbs_Edge ) {
4759       // create a ceiling edge
4760       if ( !isQuadratic ) {
4761         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4762                                vecNewNodes[ 1 ]->second.back())) {
4763           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4764                                                       vecNewNodes[ 1 ]->second.back()));
4765           srcElements.push_back( elem );
4766         }
4767       }
4768       else {
4769         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4770                                vecNewNodes[ 1 ]->second.back(),
4771                                vecNewNodes[ 2 ]->second.back())) {
4772           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4773                                                       vecNewNodes[ 1 ]->second.back(),
4774                                                       vecNewNodes[ 2 ]->second.back()));
4775           srcElements.push_back( elem );
4776         }
4777       }
4778     }
4779     if ( elem->GetType() != SMDSAbs_Face )
4780       continue;
4781
4782     bool hasFreeLinks = false;
4783
4784     TIDSortedElemSet avoidSet;
4785     avoidSet.insert( elem );
4786
4787     set<const SMDS_MeshNode*> aFaceLastNodes;
4788     int iNode, nbNodes = vecNewNodes.size();
4789     if ( !isQuadratic ) {
4790       // loop on the face nodes
4791       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4792         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4793         // look for free links of the face
4794         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4795         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4796         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4797         // check if a link n1-n2 is free
4798         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4799           hasFreeLinks = true;
4800           // make a new edge and a ceiling for a new edge
4801           const SMDS_MeshElement* edge;
4802           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4803             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4804             srcElements.push_back( myLastCreatedElems.back() );
4805           }
4806           n1 = vecNewNodes[ iNode ]->second.back();
4807           n2 = vecNewNodes[ iNext ]->second.back();
4808           if ( !aMesh->FindEdge( n1, n2 )) {
4809             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4810             srcElements.push_back( edge );
4811           }
4812         }
4813       }
4814     }
4815     else { // elem is quadratic face
4816       int nbn = nbNodes/2;
4817       for ( iNode = 0; iNode < nbn; iNode++ ) {
4818         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4819         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4820         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4821         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4822         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4823         // check if a link is free
4824         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4825              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4826              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4827           hasFreeLinks = true;
4828           // make an edge and a ceiling for a new edge
4829           // find medium node
4830           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4831             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4832             srcElements.push_back( elem );
4833           }
4834           n1 = vecNewNodes[ iNode ]->second.back();
4835           n2 = vecNewNodes[ iNext ]->second.back();
4836           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4837           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4838             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4839             srcElements.push_back( elem );
4840           }
4841         }
4842       }
4843       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4844         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4845       }
4846     }
4847
4848     // sweep free links into faces
4849
4850     if ( hasFreeLinks ) {
4851       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4852       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4853
4854       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4855       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4856       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4857         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4858         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4859       }
4860       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4861         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4862         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4863       }
4864       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4865         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4866         std::advance( v, volNb );
4867         // find indices of free faces of a volume and their source edges
4868         list< int > freeInd;
4869         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4870         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4871         int iF, nbF = vTool.NbFaces();
4872         for ( iF = 0; iF < nbF; iF ++ ) {
4873           if ( vTool.IsFreeFace( iF ) &&
4874                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4875                initNodeSet != faceNodeSet) // except an initial face
4876           {
4877             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4878               continue;
4879             if ( faceNodeSet == initNodeSetNoCenter )
4880               continue;
4881             freeInd.push_back( iF );
4882             // find source edge of a free face iF
4883             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4884             vector<const SMDS_MeshNode*>::iterator lastCommom;
4885             commonNodes.resize( nbNodes, 0 );
4886             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4887                                                 initNodeSet.begin(), initNodeSet.end(),
4888                                                 commonNodes.begin());
4889             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4890               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4891             else
4892               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4893 #ifdef _DEBUG_
4894             if ( !srcEdges.back() )
4895             {
4896               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4897                    << iF << " of volume #" << vTool.ID() << endl;
4898             }
4899 #endif
4900           }
4901         }
4902         if ( freeInd.empty() )
4903           continue;
4904
4905         // create wall faces for all steps;
4906         // if such a face has been already created by sweep of edge,
4907         // assure that its orientation is OK
4908         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4909         {
4910           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4911           vTool.SetExternalNormal();
4912           const int nextShift = vTool.IsForward() ? +1 : -1;
4913           list< int >::iterator ind = freeInd.begin();
4914           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4915           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4916           {
4917             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4918             int nbn = vTool.NbFaceNodes( *ind );
4919             const SMDS_MeshElement * f = 0;
4920             if ( nbn == 3 )              ///// triangle
4921             {
4922               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4923               if ( !f ||
4924                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4925               {
4926                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4927                                                      nodes[ 1 ],
4928                                                      nodes[ 1 + nextShift ] };
4929                 if ( f )
4930                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4931                 else
4932                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4933                                                                newOrder[ 2 ] ));
4934               }
4935             }
4936             else if ( nbn == 4 )       ///// quadrangle
4937             {
4938               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4939               if ( !f ||
4940                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4943                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4944                 if ( f )
4945                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4946                 else
4947                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4948                                                                newOrder[ 2 ], newOrder[ 3 ]));
4949               }
4950             }
4951             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4952             {
4953               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4954               if ( !f ||
4955                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4956               {
4957                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4958                                                      nodes[2],
4959                                                      nodes[2 + 2*nextShift],
4960                                                      nodes[3 - 2*nextShift],
4961                                                      nodes[3],
4962                                                      nodes[3 + 2*nextShift]};
4963                 if ( f )
4964                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4965                 else
4966                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4967                                                                newOrder[ 1 ],
4968                                                                newOrder[ 2 ],
4969                                                                newOrder[ 3 ],
4970                                                                newOrder[ 4 ],
4971                                                                newOrder[ 5 ] ));
4972               }
4973             }
4974             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4975             {
4976               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4977                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4978               if ( !f ||
4979                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4980               {
4981                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4982                                                      nodes[4 - 2*nextShift],
4983                                                      nodes[4],
4984                                                      nodes[4 + 2*nextShift],
4985                                                      nodes[1],
4986                                                      nodes[5 - 2*nextShift],
4987                                                      nodes[5],
4988                                                      nodes[5 + 2*nextShift] };
4989                 if ( f )
4990                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4991                 else
4992                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4993                                                               newOrder[ 2 ], newOrder[ 3 ],
4994                                                               newOrder[ 4 ], newOrder[ 5 ],
4995                                                               newOrder[ 6 ], newOrder[ 7 ]));
4996               }
4997             }
4998             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4999             {
5000               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5001                                       SMDSAbs_Face, /*noMedium=*/false);
5002               if ( !f ||
5003                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5004               {
5005                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5006                                                      nodes[4 - 2*nextShift],
5007                                                      nodes[4],
5008                                                      nodes[4 + 2*nextShift],
5009                                                      nodes[1],
5010                                                      nodes[5 - 2*nextShift],
5011                                                      nodes[5],
5012                                                      nodes[5 + 2*nextShift],
5013                                                      nodes[8] };
5014                 if ( f )
5015                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5016                 else
5017                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5018                                                               newOrder[ 2 ], newOrder[ 3 ],
5019                                                               newOrder[ 4 ], newOrder[ 5 ],
5020                                                               newOrder[ 6 ], newOrder[ 7 ],
5021                                                               newOrder[ 8 ]));
5022               }
5023             }
5024             else  //////// polygon
5025             {
5026               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5027               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5028               if ( !f ||
5029                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5030               {
5031                 if ( !vTool.IsForward() )
5032                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5033                 if ( f )
5034                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5035                 else
5036                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5037               }
5038             }
5039
5040             while ( srcElements.size() < myLastCreatedElems.size() )
5041               srcElements.push_back( *srcEdge );
5042
5043           }  // loop on free faces
5044
5045           // go to the next volume
5046           iVol = 0;
5047           while ( iVol++ < nbVolumesByStep ) v++;
5048
5049         } // loop on steps
5050       } // loop on volumes of one step
5051     } // sweep free links into faces
5052
5053     // Make a ceiling face with a normal external to a volume
5054
5055     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5056     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5057     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5058
5059     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5060       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5061       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5062     }
5063     if ( iF >= 0 )
5064     {
5065       lastVol.SetExternalNormal();
5066       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5067       const               int nbn = lastVol.NbFaceNodes( iF );
5068       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5069       if ( !hasFreeLinks ||
5070            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5071       {
5072         const vector<int>& interlace =
5073           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5074         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5075
5076         AddElement( nodeVec, anyFace.Init( elem ));
5077
5078         while ( srcElements.size() < myLastCreatedElems.size() )
5079           srcElements.push_back( elem );
5080       }
5081     }
5082   } // loop on swept elements
5083 }
5084
5085 //=======================================================================
5086 //function : RotationSweep
5087 //purpose  :
5088 //=======================================================================
5089
5090 SMESH_MeshEditor::PGroupIDs
5091 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5092                                 const gp_Ax1&      theAxis,
5093                                 const double       theAngle,
5094                                 const int          theNbSteps,
5095                                 const double       theTol,
5096                                 const bool         theMakeGroups,
5097                                 const bool         theMakeWalls)
5098 {
5099   ClearLastCreated();
5100
5101   setElemsFirst( theElemSets );
5102   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5103   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5104
5105   // source elements for each generated one
5106   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5107   srcElems.reserve( theElemSets[0].size() );
5108   srcNodes.reserve( theElemSets[1].size() );
5109
5110   gp_Trsf aTrsf;
5111   aTrsf.SetRotation( theAxis, theAngle );
5112   gp_Trsf aTrsf2;
5113   aTrsf2.SetRotation( theAxis, theAngle/2. );
5114
5115   gp_Lin aLine( theAxis );
5116   double aSqTol = theTol * theTol;
5117
5118   SMESHDS_Mesh* aMesh = GetMeshDS();
5119
5120   TNodeOfNodeListMap mapNewNodes;
5121   TElemOfVecOfNnlmiMap mapElemNewNodes;
5122   TTElemOfElemListMap newElemsMap;
5123
5124   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5125                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5126                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5127   // loop on theElemSets
5128   TIDSortedElemSet::iterator itElem;
5129   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5130   {
5131     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5132     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5133       const SMDS_MeshElement* elem = *itElem;
5134       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5135         continue;
5136       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5137       newNodesItVec.reserve( elem->NbNodes() );
5138
5139       // loop on elem nodes
5140       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5141       while ( itN->more() )
5142       {
5143         const SMDS_MeshNode* node = cast2Node( itN->next() );
5144
5145         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5146         double coord[3];
5147         aXYZ.Coord( coord[0], coord[1], coord[2] );
5148         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5149
5150         // check if a node has been already sweeped
5151         TNodeOfNodeListMapItr nIt =
5152           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5153         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5154         if ( listNewNodes.empty() )
5155         {
5156           // check if we are to create medium nodes between corner ones
5157           bool needMediumNodes = false;
5158           if ( isQuadraticMesh )
5159           {
5160             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5161             while (it->more() && !needMediumNodes )
5162             {
5163               const SMDS_MeshElement* invElem = it->next();
5164               if ( invElem != elem && !theElems.count( invElem )) continue;
5165               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5166               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5167                 needMediumNodes = true;
5168             }
5169           }
5170
5171           // make new nodes
5172           const SMDS_MeshNode * newNode = node;
5173           for ( int i = 0; i < theNbSteps; i++ ) {
5174             if ( !isOnAxis ) {
5175               if ( needMediumNodes )  // create a medium node
5176               {
5177                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5178                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5179                 myLastCreatedNodes.push_back(newNode);
5180                 srcNodes.push_back( node );
5181                 listNewNodes.push_back( newNode );
5182                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5183               }
5184               else {
5185                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5186               }
5187               // create a corner node
5188               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5189               myLastCreatedNodes.push_back(newNode);
5190               srcNodes.push_back( node );
5191               listNewNodes.push_back( newNode );
5192             }
5193             else {
5194               listNewNodes.push_back( newNode );
5195               // if ( needMediumNodes )
5196               //   listNewNodes.push_back( newNode );
5197             }
5198           }
5199         }
5200         newNodesItVec.push_back( nIt );
5201       }
5202       // make new elements
5203       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5204     }
5205   }
5206
5207   if ( theMakeWalls )
5208     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5209
5210   PGroupIDs newGroupIDs;
5211   if ( theMakeGroups )
5212     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5213
5214   return newGroupIDs;
5215 }
5216
5217 //=======================================================================
5218 //function : ExtrusParam
5219 //purpose  : standard construction
5220 //=======================================================================
5221
5222 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5223                                             const int                theNbSteps,
5224                                             const std::list<double>& theScales,
5225                                             const std::list<double>& theAngles,
5226                                             const gp_XYZ*            theBasePoint,
5227                                             const int                theFlags,
5228                                             const double             theTolerance):
5229   myDir( theStep ),
5230   myBaseP( Precision::Infinite(), 0, 0 ),
5231   myFlags( theFlags ),
5232   myTolerance( theTolerance ),
5233   myElemsToUse( NULL )
5234 {
5235   mySteps = new TColStd_HSequenceOfReal;
5236   const double stepSize = theStep.Magnitude();
5237   for (int i=1; i<=theNbSteps; i++ )
5238     mySteps->Append( stepSize );
5239
5240   if ( !theScales.empty() )
5241   {
5242     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5243       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5244
5245     // add medium scales
5246     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5247     myScales.reserve( theNbSteps * 2 );
5248     myScales.push_back( 0.5 * ( *s1 + 1. ));
5249     myScales.push_back( *s1 );
5250     for ( ; s2 != theScales.end(); s1 = s2++ )
5251     {
5252       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5253       myScales.push_back( *s2 );
5254     }
5255   }
5256
5257   if ( !theAngles.empty() )
5258   {
5259     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5260     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5261       linearAngleVariation( theNbSteps, angles );
5262
5263     // accumulate angles
5264     double angle = 0;
5265     int nbAngles = 0;
5266     std::list<double>::iterator a1 = angles.begin(), a2;
5267     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5268     {
5269       angle += *a1;
5270       *a1 = angle;
5271     }
5272     while ( nbAngles++ < theNbSteps )
5273       angles.push_back( angles.back() );
5274
5275     // add medium angles
5276     a2 = angles.begin(), a1 = a2++;
5277     myAngles.push_back( 0.5 * *a1 );
5278     myAngles.push_back( *a1 );
5279     for ( ; a2 != angles.end(); a1 = a2++ )
5280     {
5281       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5282       myAngles.push_back( *a2 );
5283     }
5284   }
5285
5286   if ( theBasePoint )
5287   {
5288     myBaseP = *theBasePoint;
5289   }
5290
5291   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5292       ( theTolerance > 0 ))
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5295   }
5296   else
5297   {
5298     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5299   }
5300 }
5301
5302 //=======================================================================
5303 //function : ExtrusParam
5304 //purpose  : steps are given explicitly
5305 //=======================================================================
5306
5307 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5308                                             Handle(TColStd_HSequenceOfReal) theSteps,
5309                                             const int                       theFlags,
5310                                             const double                    theTolerance):
5311   myDir( theDir ),
5312   mySteps( theSteps ),
5313   myFlags( theFlags ),
5314   myTolerance( theTolerance ),
5315   myElemsToUse( NULL )
5316 {
5317   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5318       ( theTolerance > 0 ))
5319   {
5320     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5321   }
5322   else
5323   {
5324     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5325   }
5326 }
5327
5328 //=======================================================================
5329 //function : ExtrusParam
5330 //purpose  : for extrusion by normal
5331 //=======================================================================
5332
5333 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5334                                             const int    theNbSteps,
5335                                             const int    theFlags,
5336                                             const int    theDim ):
5337   myDir( 1,0,0 ),
5338   mySteps( new TColStd_HSequenceOfReal ),
5339   myFlags( theFlags ),
5340   myTolerance( 0 ),
5341   myElemsToUse( NULL )
5342 {
5343   for (int i = 0; i < theNbSteps; i++ )
5344     mySteps->Append( theStepSize );
5345
5346   if ( theDim == 1 )
5347   {
5348     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5349   }
5350   else
5351   {
5352     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5353   }
5354 }
5355
5356 //=======================================================================
5357 //function : ExtrusParam
5358 //purpose  : for extrusion along path
5359 //=======================================================================
5360
5361 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5362                                             const gp_Pnt*                   theBasePoint,
5363                                             const std::list<double>&        theScales,
5364                                             const bool                      theMakeGroups )
5365   : myBaseP( Precision::Infinite(), 0, 0 ),
5366     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5367     myPathPoints( thePoints )
5368 {
5369   if ( theBasePoint )
5370   {
5371     myBaseP = theBasePoint->XYZ();
5372   }
5373
5374   if ( !theScales.empty() )
5375   {
5376     // add medium scales
5377     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5378     myScales.reserve( thePoints.size() * 2 );
5379     myScales.push_back( 0.5 * ( 1. + *s1 ));
5380     myScales.push_back( *s1 );
5381     for ( ; s2 != theScales.end(); s1 = s2++ )
5382     {
5383       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5384       myScales.push_back( *s2 );
5385     }
5386   }
5387
5388   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5389 }
5390
5391 //=======================================================================
5392 //function : ExtrusParam::SetElementsToUse
5393 //purpose  : stores elements to use for extrusion by normal, depending on
5394 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5395 //           define myBaseP for scaling
5396 //=======================================================================
5397
5398 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5399                                                       const TIDSortedElemSet& nodes )
5400 {
5401   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5402
5403   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5404   {
5405     myBaseP.SetCoord( 0.,0.,0. );
5406     TIDSortedElemSet newNodes;
5407
5408     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5409     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5410     {
5411       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5412       TIDSortedElemSet::const_iterator itElem = elements.begin();
5413       for ( ; itElem != elements.end(); itElem++ )
5414       {
5415         const SMDS_MeshElement* elem = *itElem;
5416         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5417         while ( itN->more() ) {
5418           const SMDS_MeshElement* node = itN->next();
5419           if ( newNodes.insert( node ).second )
5420             myBaseP += SMESH_NodeXYZ( node );
5421         }
5422       }
5423     }
5424     myBaseP /= newNodes.size();
5425   }
5426 }
5427
5428 //=======================================================================
5429 //function : ExtrusParam::beginStepIter
5430 //purpose  : prepare iteration on steps
5431 //=======================================================================
5432
5433 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5434 {
5435   myWithMediumNodes = withMediumNodes;
5436   myNextStep = 1;
5437   myCurSteps.clear();
5438 }
5439 //=======================================================================
5440 //function : ExtrusParam::moreSteps
5441 //purpose  : are there more steps?
5442 //=======================================================================
5443
5444 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5445 {
5446   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5447 }
5448 //=======================================================================
5449 //function : ExtrusParam::nextStep
5450 //purpose  : returns the next step
5451 //=======================================================================
5452
5453 double SMESH_MeshEditor::ExtrusParam::nextStep()
5454 {
5455   double res = 0;
5456   if ( !myCurSteps.empty() )
5457   {
5458     res = myCurSteps.back();
5459     myCurSteps.pop_back();
5460   }
5461   else if ( myNextStep <= mySteps->Length() )
5462   {
5463     myCurSteps.push_back( mySteps->Value( myNextStep ));
5464     ++myNextStep;
5465     if ( myWithMediumNodes )
5466     {
5467       myCurSteps.back() /= 2.;
5468       myCurSteps.push_back( myCurSteps.back() );
5469     }
5470     res = nextStep();
5471   }
5472   return res;
5473 }
5474
5475 //=======================================================================
5476 //function : ExtrusParam::makeNodesByDir
5477 //purpose  : create nodes for standard extrusion
5478 //=======================================================================
5479
5480 int SMESH_MeshEditor::ExtrusParam::
5481 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5482                 const SMDS_MeshNode*              srcNode,
5483                 std::list<const SMDS_MeshNode*> & newNodes,
5484                 const bool                        makeMediumNodes)
5485 {
5486   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5487
5488   int nbNodes = 0;
5489   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5490   {
5491     p += myDir.XYZ() * nextStep();
5492     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5493     newNodes.push_back( newNode );
5494   }
5495
5496   if ( !myScales.empty() || !myAngles.empty() )
5497   {
5498     gp_XYZ  center = myBaseP;
5499     gp_Ax1  ratationAxis( center, myDir );
5500     gp_Trsf rotation;
5501
5502     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5503     size_t i = !makeMediumNodes;
5504     for ( beginStepIter( makeMediumNodes );
5505           moreSteps();
5506           ++nIt, i += 1 + !makeMediumNodes )
5507     {
5508       center += myDir.XYZ() * nextStep();
5509
5510       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5511       bool moved = false;
5512       if ( i < myScales.size() )
5513       {
5514         xyz = ( myScales[i] * ( xyz - center )) + center;
5515         moved = true;
5516       }
5517       if ( !myAngles.empty() )
5518       {
5519         rotation.SetRotation( ratationAxis, myAngles[i] );
5520         rotation.Transforms( xyz );
5521         moved = true;
5522       }
5523       if ( moved )
5524         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5525       else
5526         break;
5527     }
5528   }
5529   return nbNodes;
5530 }
5531
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByDirAndSew
5534 //purpose  : create nodes for standard extrusion with sewing
5535 //=======================================================================
5536
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5539                       const SMDS_MeshNode*              srcNode,
5540                       std::list<const SMDS_MeshNode*> & newNodes,
5541                       const bool                        makeMediumNodes)
5542 {
5543   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5544
5545   int nbNodes = 0;
5546   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5547   {
5548     P1 += myDir.XYZ() * nextStep();
5549
5550     // try to search in sequence of existing nodes
5551     // if myNodes.size()>0 we 'nave to use given sequence
5552     // else - use all nodes of mesh
5553     const SMDS_MeshNode * node = 0;
5554     if ( myNodes.Length() > 0 )
5555     {
5556       for ( int i = 1; i <= myNodes.Length(); i++ )
5557       {
5558         SMESH_NodeXYZ P2 = myNodes.Value(i);
5559         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5560         {
5561           node = myNodes.Value(i);
5562           break;
5563         }
5564       }
5565     }
5566     else
5567     {
5568       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5569       while(itn->more())
5570       {
5571         SMESH_NodeXYZ P2 = itn->next();
5572         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5573         {
5574           node = P2._node;
5575           break;
5576         }
5577       }
5578     }
5579
5580     if ( !node )
5581       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5582
5583     newNodes.push_back( node );
5584
5585   } // loop on steps
5586
5587   return nbNodes;
5588 }
5589
5590 //=======================================================================
5591 //function : ExtrusParam::makeNodesByNormal2D
5592 //purpose  : create nodes for extrusion using normals of faces
5593 //=======================================================================
5594
5595 int SMESH_MeshEditor::ExtrusParam::
5596 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5597                      const SMDS_MeshNode*              srcNode,
5598                      std::list<const SMDS_MeshNode*> & newNodes,
5599                      const bool                        makeMediumNodes)
5600 {
5601   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5602
5603   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5604
5605   // get normals to faces sharing srcNode
5606   vector< gp_XYZ > norms, baryCenters;
5607   gp_XYZ norm, avgNorm( 0,0,0 );
5608   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5609   while ( faceIt->more() )
5610   {
5611     const SMDS_MeshElement* face = faceIt->next();
5612     if ( myElemsToUse && !myElemsToUse->count( face ))
5613       continue;
5614     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5615     {
5616       norms.push_back( norm );
5617       avgNorm += norm;
5618       if ( !alongAvgNorm )
5619       {
5620         gp_XYZ bc(0,0,0);
5621         int nbN = 0;
5622         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5623           bc += SMESH_NodeXYZ( nIt->next() );
5624         baryCenters.push_back( bc / nbN );
5625       }
5626     }
5627   }
5628
5629   if ( norms.empty() ) return 0;
5630
5631   double normSize = avgNorm.Modulus();
5632   if ( normSize < std::numeric_limits<double>::min() )
5633     return 0;
5634
5635   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5636   {
5637     myDir = avgNorm;
5638     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5639   }
5640
5641   avgNorm /= normSize;
5642
5643   int nbNodes = 0;
5644   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5645   {
5646     gp_XYZ pNew = p;
5647     double stepSize = nextStep();
5648
5649     if ( norms.size() > 1 )
5650     {
5651       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5652       {
5653         // translate plane of a face
5654         baryCenters[ iF ] += norms[ iF ] * stepSize;
5655
5656         // find point of intersection of the face plane located at baryCenters[ iF ]
5657         // and avgNorm located at pNew
5658         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5659         double dot  = ( norms[ iF ] * avgNorm );
5660         if ( dot < std::numeric_limits<double>::min() )
5661           dot = stepSize * 1e-3;
5662         double step = -( norms[ iF ] * pNew + d ) / dot;
5663         pNew += step * avgNorm;
5664       }
5665     }
5666     else
5667     {
5668       pNew += stepSize * avgNorm;
5669     }
5670     p = pNew;
5671
5672     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5673     newNodes.push_back( newNode );
5674   }
5675   return nbNodes;
5676 }
5677
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesByNormal1D
5680 //purpose  : create nodes for extrusion using normals of edges
5681 //=======================================================================
5682
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5685                      const SMDS_MeshNode*              /*srcNode*/,
5686                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5687                      const bool                        /*makeMediumNodes*/)
5688 {
5689   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5690   return 0;
5691 }
5692
5693 //=======================================================================
5694 //function : ExtrusParam::makeNodesAlongTrack
5695 //purpose  : create nodes for extrusion along path
5696 //=======================================================================
5697
5698 int SMESH_MeshEditor::ExtrusParam::
5699 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5700                      const SMDS_MeshNode*              srcNode,
5701                      std::list<const SMDS_MeshNode*> & newNodes,
5702                      const bool                        makeMediumNodes)
5703 {
5704   const Standard_Real aTolAng=1.e-4;
5705
5706   gp_Pnt aV0x = myBaseP;
5707   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5708
5709   const PathPoint& aPP0 = myPathPoints[0];
5710   gp_Pnt aP0x = aPP0.myPnt;
5711   gp_Dir aDT0x= aPP0.myTgt;
5712
5713   std::vector< gp_Pnt > centers;
5714   centers.reserve( NbSteps() * 2 );
5715
5716   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5717
5718   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5719   {
5720     const PathPoint&  aPP  = myPathPoints[j];
5721     const gp_Pnt&     aP1x = aPP.myPnt;
5722     const gp_Dir&    aDT1x = aPP.myTgt;
5723
5724     // Translation
5725     gp_Vec aV01x( aP0x, aP1x );
5726     aTrsf.SetTranslation( aV01x );
5727     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5728     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5729
5730     // rotation 1 [ T1,T0 ]
5731     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5732     if ( fabs( aAngleT1T0 ) > aTolAng )
5733     {
5734       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5735       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5736
5737       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5738     }
5739
5740     // rotation 2
5741     if ( aPP.myAngle != 0. )
5742     {
5743       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5744       aPN1 = aPN1.Transformed( aTrsfRot );
5745     }
5746
5747     // make new node
5748     if ( makeMediumNodes )
5749     {
5750       // create additional node
5751       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5752       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5753       newNodes.push_back( newNode );
5754
5755     }
5756     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5757     newNodes.push_back( newNode );
5758
5759     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5760     centers.push_back( aV1x );
5761
5762     aPN0 = aPN1;
5763     aP0x = aP1x;
5764     aV0x = aV1x;
5765     aDT0x = aDT1x;
5766   }
5767
5768   // scale
5769   if ( !myScales.empty() )
5770   {
5771     gp_Trsf aTrsfScale;
5772     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5773     for ( size_t i = !makeMediumNodes;
5774           i < myScales.size() && node != newNodes.end();
5775           i += ( 1 + !makeMediumNodes ), ++node )
5776     {
5777       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5778       gp_Pnt aN = SMESH_NodeXYZ( *node );
5779       gp_Pnt aP = aN.Transformed( aTrsfScale );
5780       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5781     }
5782   }
5783
5784   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5785 }
5786
5787 //=======================================================================
5788 //function : ExtrusionSweep
5789 //purpose  :
5790 //=======================================================================
5791
5792 SMESH_MeshEditor::PGroupIDs
5793 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5794                                   const gp_Vec&        theStep,
5795                                   const int            theNbSteps,
5796                                   TTElemOfElemListMap& newElemsMap,
5797                                   const int            theFlags,
5798                                   const double         theTolerance)
5799 {
5800   std::list<double> dummy;
5801   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5802                        theFlags, theTolerance );
5803   return ExtrusionSweep( theElems, aParams, newElemsMap );
5804 }
5805
5806 namespace
5807 {
5808
5809 //=======================================================================
5810 //function : getOriFactor
5811 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5812 //           edge curve orientation
5813 //=======================================================================
5814
5815   double getOriFactor( const TopoDS_Edge&   edge,
5816                        const SMDS_MeshNode* n1,
5817                        const SMDS_MeshNode* n2,
5818                        SMESH_MesherHelper&  helper)
5819   {
5820     double u1 = helper.GetNodeU( edge, n1, n2 );
5821     double u2 = helper.GetNodeU( edge, n2, n1 );
5822     return u1 < u2 ? 1. : -1.;
5823   }
5824 }
5825
5826 //=======================================================================
5827 //function : ExtrusionSweep
5828 //purpose  :
5829 //=======================================================================
5830
5831 SMESH_MeshEditor::PGroupIDs
5832 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5833                                   ExtrusParam&         theParams,
5834                                   TTElemOfElemListMap& newElemsMap)
5835 {
5836   ClearLastCreated();
5837
5838   setElemsFirst( theElemSets );
5839   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5840   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5841
5842   // source elements for each generated one
5843   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5844   srcElems.reserve( theElemSets[0].size() );
5845   srcNodes.reserve( theElemSets[1].size() );
5846
5847   const int nbSteps = theParams.NbSteps();
5848   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5849
5850   TNodeOfNodeListMap   mapNewNodes;
5851   TElemOfVecOfNnlmiMap mapElemNewNodes;
5852
5853   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5854                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5855                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5856   // loop on theElems
5857   TIDSortedElemSet::iterator itElem;
5858   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5859   {
5860     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5861     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5862     {
5863       // check element type
5864       const SMDS_MeshElement* elem = *itElem;
5865       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5866         continue;
5867
5868       const size_t nbNodes = elem->NbNodes();
5869       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5870       newNodesItVec.reserve( nbNodes );
5871
5872       // loop on elem nodes
5873       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5874       while ( itN->more() )
5875       {
5876         // check if a node has been already sweeped
5877         const SMDS_MeshNode* node = itN->next();
5878         TNodeOfNodeListMap::iterator nIt =
5879           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5880         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5881         if ( listNewNodes.empty() )
5882         {
5883           // make new nodes
5884
5885           // check if we are to create medium nodes between corner ones
5886           bool needMediumNodes = false;
5887           if ( isQuadraticMesh )
5888           {
5889             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5890             while (it->more() && !needMediumNodes )
5891             {
5892               const SMDS_MeshElement* invElem = it->next();
5893               if ( invElem != elem && !theElems.count( invElem )) continue;
5894               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5895               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5896                 needMediumNodes = true;
5897             }
5898           }
5899           // create nodes for all steps
5900           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5901           {
5902             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5903             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5904             {
5905               myLastCreatedNodes.push_back( *newNodesIt );
5906               srcNodes.push_back( node );
5907             }
5908           }
5909           else
5910           {
5911             if ( theParams.ToMakeBoundary() )
5912             {
5913               GetMeshDS()->Modified();
5914               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5915             }
5916             break; // newNodesItVec will be shorter than nbNodes
5917           }
5918         }
5919         newNodesItVec.push_back( nIt );
5920       }
5921       // make new elements
5922       if ( newNodesItVec.size() == nbNodes )
5923         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5924     }
5925   }
5926
5927   if ( theParams.ToMakeBoundary() ) {
5928     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5929   }
5930   PGroupIDs newGroupIDs;
5931   if ( theParams.ToMakeGroups() )
5932     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5933
5934   return newGroupIDs;
5935 }
5936
5937 //=======================================================================
5938 //function : ExtrusionAlongTrack
5939 //purpose  :
5940 //=======================================================================
5941 SMESH_MeshEditor::Extrusion_Error
5942 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5943                                        SMESH_Mesh*          theTrackMesh,
5944                                        SMDS_ElemIteratorPtr theTrackIterator,
5945                                        const SMDS_MeshNode* theN1,
5946                                        std::list<double>&   theAngles,
5947                                        const bool           theAngleVariation,
5948                                        std::list<double>&   theScales,
5949                                        const bool           theScaleVariation,
5950                                        const gp_Pnt*        theRefPoint,
5951                                        const bool           theMakeGroups)
5952 {
5953   ClearLastCreated();
5954
5955   // 1. Check data
5956   if ( theElements[0].empty() && theElements[1].empty() )
5957     return EXTR_NO_ELEMENTS;
5958
5959   ASSERT( theTrackMesh );
5960   if ( ! theTrackIterator || !theTrackIterator->more() )
5961     return EXTR_NO_ELEMENTS;
5962
5963   // 2. Get ordered nodes
5964   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5965   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5966   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5967   if ( branchEdges.empty() )
5968     return EXTR_PATH_NOT_EDGE;
5969
5970   if ( branchEdges.size() > 1 )
5971     return EXTR_BAD_PATH_SHAPE;
5972
5973   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5974   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5975   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5976     return EXTR_BAD_STARTING_NODE;
5977
5978   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5979   {
5980     // add medium nodes to pathNodes
5981     std::vector< const SMDS_MeshNode* >    pathNodes2;
5982     std::vector< const SMDS_MeshElement* > pathEdges2;
5983     pathNodes2.reserve( pathNodes.size() * 2 );
5984     pathEdges2.reserve( pathEdges.size() * 2 );
5985     for ( size_t i = 0; i < pathEdges.size(); ++i )
5986     {
5987       pathNodes2.push_back( pathNodes[i] );
5988       pathEdges2.push_back( pathEdges[i] );
5989       if ( pathEdges[i]->IsQuadratic() )
5990       {
5991         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5992         pathEdges2.push_back( pathEdges[i] );
5993       }
5994     }
5995     pathNodes2.push_back( pathNodes.back() );
5996     pathEdges.swap( pathEdges2 );
5997     pathNodes.swap( pathNodes2 );
5998   }
5999
6000   // 3. Get path data at pathNodes
6001
6002   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6003
6004   if ( theAngleVariation )
6005     linearAngleVariation( points.size()-1, theAngles );
6006   if ( theScaleVariation )
6007     linearScaleVariation( points.size()-1, theScales );
6008
6009   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6010   std::list<double>::iterator angle = theAngles.begin();
6011
6012   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6013
6014   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6015   std::map< int, double >::iterator id2factor;
6016   SMESH_MesherHelper pathHelper( *theTrackMesh );
6017   gp_Pnt p; gp_Vec tangent;
6018   const double tol2 = gp::Resolution() * gp::Resolution();
6019
6020   for ( size_t i = 0; i < pathNodes.size(); ++i )
6021   {
6022     ExtrusParam::PathPoint & point = points[ i ];
6023
6024     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6025
6026     if ( angle != theAngles.end() )
6027       point.myAngle = *angle++;
6028
6029     tangent.SetCoord( 0,0,0 );
6030     const int          shapeID = pathNodes[ i ]->GetShapeID();
6031     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6032     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6033     switch ( shapeType )
6034     {
6035     case TopAbs_EDGE:
6036     {
6037       TopoDS_Edge edge = TopoDS::Edge( shape );
6038       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6039       if ( id2factor->second == 0 )
6040       {
6041         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6042         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6043       }
6044       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6045       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6046       curve->D1( u, p, tangent );
6047       tangent *= id2factor->second;
6048       break;
6049     }
6050     case TopAbs_VERTEX:
6051     {
6052       int nbEdges = 0;
6053       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6054       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6055       {
6056         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6057         for ( int di = -1; di <= 0; ++di )
6058         {
6059           size_t j = i + di;
6060           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6061           {
6062             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6063             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6064             if ( id2factor->second == 0 )
6065             {
6066               if ( j < i )
6067                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6068               else
6069                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6070             }
6071             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6072             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6073             gp_Vec du;
6074             curve->D1( u, p, du );
6075             double size2 = du.SquareMagnitude();
6076             if ( du.SquareMagnitude() > tol2 )
6077             {
6078               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6079               nbEdges++;
6080             }
6081             break;
6082           }
6083         }
6084       }
6085       if ( nbEdges > 0 )
6086         break;
6087     }
6088     // fall through
6089     default:
6090     {
6091       for ( int di = -1; di <= 1; di += 2 )
6092       {
6093         size_t j = i + di;
6094         if ( j < pathNodes.size() )
6095         {
6096           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6097           double size2 = dir.SquareMagnitude();
6098           if ( size2 > tol2 )
6099             tangent += dir.Divided( Sqrt( size2 )) * di;
6100         }
6101       }
6102     }
6103     } // switch ( shapeType )
6104
6105     if ( tangent.SquareMagnitude() < tol2 )
6106       return EXTR_CANT_GET_TANGENT;
6107
6108     point.myTgt = tangent;
6109
6110   } // loop on pathNodes
6111
6112
6113   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6114   TTElemOfElemListMap newElemsMap;
6115
6116   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6117
6118   return EXTR_OK;
6119 }
6120
6121 //=======================================================================
6122 //function : linearAngleVariation
6123 //purpose  : spread values over nbSteps
6124 //=======================================================================
6125
6126 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6127                                             list<double>& Angles)
6128 {
6129   int nbAngles = Angles.size();
6130   if( nbSteps > nbAngles && nbAngles > 0 )
6131   {
6132     vector<double> theAngles(nbAngles);
6133     theAngles.assign( Angles.begin(), Angles.end() );
6134
6135     list<double> res;
6136     double rAn2St = double( nbAngles ) / double( nbSteps );
6137     double angPrev = 0, angle;
6138     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6139     {
6140       double angCur = rAn2St * ( iSt+1 );
6141       double angCurFloor  = floor( angCur );
6142       double angPrevFloor = floor( angPrev );
6143       if ( angPrevFloor == angCurFloor )
6144         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6145       else {
6146         int iP = int( angPrevFloor );
6147         double angPrevCeil = ceil(angPrev);
6148         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6149
6150         int iC = int( angCurFloor );
6151         if ( iC < nbAngles )
6152           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6153
6154         iP = int( angPrevCeil );
6155         while ( iC-- > iP )
6156           angle += theAngles[ iC ];
6157       }
6158       res.push_back(angle);
6159       angPrev = angCur;
6160     }
6161     Angles.swap( res );
6162   }
6163 }
6164
6165 //=======================================================================
6166 //function : linearScaleVariation
6167 //purpose  : spread values over nbSteps 
6168 //=======================================================================
6169
6170 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6171                                             std::list<double>& theScales)
6172 {
6173   int nbScales = theScales.size();
6174   std::vector<double> myScales;
6175   myScales.reserve( theNbSteps );
6176   std::list<double>::const_iterator scale = theScales.begin();
6177   double prevScale = 1.0;
6178   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6179   {
6180     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6181     int    stDelta = Max( 1, iStep - myScales.size());
6182     double scDelta = ( *scale - prevScale ) / stDelta;
6183     for ( int iStep = 0; iStep < stDelta; ++iStep )
6184     {
6185       myScales.push_back( prevScale + scDelta );
6186       prevScale = myScales.back();
6187     }
6188     prevScale = *scale;
6189   }
6190   theScales.assign( myScales.begin(), myScales.end() );
6191 }
6192
6193 //================================================================================
6194 /*!
6195  * \brief Move or copy theElements applying theTrsf to their nodes
6196  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6197  *  \param theTrsf - transformation to apply
6198  *  \param theCopy - if true, create translated copies of theElems
6199  *  \param theMakeGroups - if true and theCopy, create translated groups
6200  *  \param theTargetMesh - mesh to copy translated elements into
6201  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6202  */
6203 //================================================================================
6204
6205 SMESH_MeshEditor::PGroupIDs
6206 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6207                              const gp_Trsf&     theTrsf,
6208                              const bool         theCopy,
6209                              const bool         theMakeGroups,
6210                              SMESH_Mesh*        theTargetMesh)
6211 {
6212   ClearLastCreated();
6213   myLastCreatedElems.reserve( theElems.size() );
6214
6215   bool needReverse = false;
6216   string groupPostfix;
6217   switch ( theTrsf.Form() ) {
6218   case gp_PntMirror:
6219     needReverse = true;
6220     groupPostfix = "mirrored";
6221     break;
6222   case gp_Ax1Mirror:
6223     groupPostfix = "mirrored";
6224     break;
6225   case gp_Ax2Mirror:
6226     needReverse = true;
6227     groupPostfix = "mirrored";
6228     break;
6229   case gp_Rotation:
6230     groupPostfix = "rotated";
6231     break;
6232   case gp_Translation:
6233     groupPostfix = "translated";
6234     break;
6235   case gp_Scale:
6236     groupPostfix = "scaled";
6237     break;
6238   case gp_CompoundTrsf: // different scale by axis
6239     groupPostfix = "scaled";
6240     break;
6241   default:
6242     needReverse = false;
6243     groupPostfix = "transformed";
6244   }
6245
6246   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6247   SMESHDS_Mesh* aMesh    = GetMeshDS();
6248
6249   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6250   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6251   SMESH_MeshEditor::ElemFeatures elemType;
6252
6253   // map old node to new one
6254   TNodeNodeMap nodeMap;
6255
6256   // elements sharing moved nodes; those of them which have all
6257   // nodes mirrored but are not in theElems are to be reversed
6258   TIDSortedElemSet inverseElemSet;
6259
6260   // source elements for each generated one
6261   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6262
6263   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6264   TIDSortedElemSet orphanNode;
6265
6266   if ( theElems.empty() ) // transform the whole mesh
6267   {
6268     // add all elements
6269     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6270     while ( eIt->more() ) theElems.insert( eIt->next() );
6271     // add orphan nodes
6272     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6273     while ( nIt->more() )
6274     {
6275       const SMDS_MeshNode* node = nIt->next();
6276       if ( node->NbInverseElements() == 0)
6277         orphanNode.insert( node );
6278     }
6279   }
6280
6281   // loop on elements to transform nodes : first orphan nodes then elems
6282   TIDSortedElemSet::iterator itElem;
6283   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6284   for (int i=0; i<2; i++)
6285     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6286     {
6287       const SMDS_MeshElement* elem = *itElem;
6288       if ( !elem )
6289         continue;
6290
6291       // loop on elem nodes
6292       double coord[3];
6293       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6294       while ( itN->more() )
6295       {
6296         const SMDS_MeshNode* node = cast2Node( itN->next() );
6297         // check if a node has been already transformed
6298         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6299           nodeMap.insert( make_pair ( node, node ));
6300         if ( !n2n_isnew.second )
6301           continue;
6302
6303         node->GetXYZ( coord );
6304         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6305         if ( theTargetMesh ) {
6306           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6307           n2n_isnew.first->second = newNode;
6308           myLastCreatedNodes.push_back(newNode);
6309           srcNodes.push_back( node );
6310         }
6311         else if ( theCopy ) {
6312           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6313           n2n_isnew.first->second = newNode;
6314           myLastCreatedNodes.push_back(newNode);
6315           srcNodes.push_back( node );
6316         }
6317         else {
6318           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6319           // node position on shape becomes invalid
6320           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6321             ( SMDS_SpacePosition::originSpacePosition() );
6322         }
6323
6324         // keep inverse elements
6325         if ( !theCopy && !theTargetMesh && needReverse ) {
6326           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6327           while ( invElemIt->more() ) {
6328             const SMDS_MeshElement* iel = invElemIt->next();
6329             inverseElemSet.insert( iel );
6330           }
6331         }
6332       }
6333     } // loop on elems in { &orphanNode, &theElems };
6334
6335   // either create new elements or reverse mirrored ones
6336   if ( !theCopy && !needReverse && !theTargetMesh )
6337     return PGroupIDs();
6338
6339   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6340
6341   // Replicate or reverse elements
6342
6343   std::vector<int> iForw;
6344   vector<const SMDS_MeshNode*> nodes;
6345   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6346   {
6347     const SMDS_MeshElement* elem = *itElem;
6348     if ( !elem ) continue;
6349
6350     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6351     size_t               nbNodes  = elem->NbNodes();
6352     if ( geomType == SMDSGeom_NONE ) continue; // node
6353
6354     nodes.resize( nbNodes );
6355
6356     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6357     {
6358       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6359       if ( !aPolyedre )
6360         continue;
6361       nodes.clear();
6362       bool allTransformed = true;
6363       int nbFaces = aPolyedre->NbFaces();
6364       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6365       {
6366         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6367         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6368         {
6369           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6370           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6371           if ( nodeMapIt == nodeMap.end() )
6372             allTransformed = false; // not all nodes transformed
6373           else
6374             nodes.push_back((*nodeMapIt).second);
6375         }
6376         if ( needReverse && allTransformed )
6377           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6378       }
6379       if ( !allTransformed )
6380         continue; // not all nodes transformed
6381     }
6382     else // ----------------------- the rest element types
6383     {
6384       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6385       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6386       const vector<int>&    i = needReverse ? iRev : iForw;
6387
6388       // find transformed nodes
6389       size_t iNode = 0;
6390       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6391       while ( itN->more() ) {
6392         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6393         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6394         if ( nodeMapIt == nodeMap.end() )
6395           break; // not all nodes transformed
6396         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6397       }
6398       if ( iNode != nbNodes )
6399         continue; // not all nodes transformed
6400     }
6401
6402     if ( editor ) {
6403       // copy in this or a new mesh
6404       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6405         srcElems.push_back( elem );
6406     }
6407     else {
6408       // reverse element as it was reversed by transformation
6409       if ( nbNodes > 2 )
6410         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6411     }
6412
6413   } // loop on elements
6414
6415   if ( editor && editor != this )
6416     myLastCreatedElems.swap( editor->myLastCreatedElems );
6417
6418   PGroupIDs newGroupIDs;
6419
6420   if ( ( theMakeGroups && theCopy ) ||
6421        ( theMakeGroups && theTargetMesh ) )
6422     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6423
6424   return newGroupIDs;
6425 }
6426
6427 //================================================================================
6428 /*!
6429  * \brief Make an offset mesh from a source 2D mesh
6430  *  \param [in] theElements - source faces
6431  *  \param [in] theValue - offset value
6432  *  \param [out] theTgtMesh - a mesh to add offset elements to
6433  *  \param [in] theMakeGroups - to generate groups
6434  *  \return PGroupIDs - IDs of created groups. NULL means failure
6435  */
6436 //================================================================================
6437
6438 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6439                                                       const double       theValue,
6440                                                       SMESH_Mesh*        theTgtMesh,
6441                                                       const bool         theMakeGroups,
6442                                                       const bool         theCopyElements,
6443                                                       const bool         theFixSelfIntersection)
6444 {
6445   SMESHDS_Mesh*    meshDS = GetMeshDS();
6446   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6447   SMESH_MeshEditor tgtEditor( theTgtMesh );
6448
6449   SMDS_ElemIteratorPtr eIt;
6450   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6452
6453   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6454   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6455   std::unique_ptr< SMDS_Mesh > offsetMesh
6456     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6457                                    theFixSelfIntersection,
6458                                    new2OldFaces, new2OldNodes ));
6459   if ( offsetMesh->NbElements() == 0 )
6460     return PGroupIDs(); // MakeOffset() failed
6461
6462
6463   if ( theTgtMesh == myMesh && !theCopyElements )
6464   {
6465     // clear the source elements
6466     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6467     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6468     while ( eIt->more() )
6469       meshDS->RemoveFreeElement( eIt->next(), 0 );
6470   }
6471
6472   // offsetMesh->Modified();
6473   // offsetMesh->CompactMesh(); // make IDs start from 1
6474
6475   // source elements for each generated one
6476   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6477   srcElems.reserve( new2OldFaces.size() );
6478   srcNodes.reserve( new2OldNodes.size() );
6479
6480   ClearLastCreated();
6481   myLastCreatedElems.reserve( new2OldFaces.size() );
6482   myLastCreatedNodes.reserve( new2OldNodes.size() );
6483
6484   // copy offsetMesh to theTgtMesh
6485
6486   smIdType idShift = meshDS->MaxNodeID();
6487   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6488     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6489     {
6490 #ifndef _DEBUG_
6491       if ( n->NbInverseElements() > 0 )
6492 #endif
6493       {
6494         const SMDS_MeshNode* n2 =
6495           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6496         myLastCreatedNodes.push_back( n2 );
6497         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6498       }
6499     }
6500
6501   ElemFeatures elemType;
6502   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6503     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6504     {
6505       elemType.Init( f );
6506       elemType.myNodes.clear();
6507       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6508       {
6509         const SMDS_MeshNode* n2 = nIt->next();
6510         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6511       }
6512       tgtEditor.AddElement( elemType.myNodes, elemType );
6513       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6514     }
6515
6516   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6517
6518   PGroupIDs newGroupIDs;
6519   if ( theMakeGroups )
6520     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6521   else
6522     newGroupIDs.reset( new std::list< int > );
6523
6524   return newGroupIDs;
6525 }
6526
6527 //=======================================================================
6528 /*!
6529  * \brief Create groups of elements made during transformation
6530  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6531  *  \param elemGens - elements making corresponding myLastCreatedElems
6532  *  \param postfix - to push_back to names of new groups
6533  *  \param targetMesh - mesh to create groups in
6534  *  \param topPresent - is there are "top" elements that are created by sweeping
6535  */
6536 //=======================================================================
6537
6538 SMESH_MeshEditor::PGroupIDs
6539 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6540                                  const SMESH_SequenceOfElemPtr& elemGens,
6541                                  const std::string&             postfix,
6542                                  SMESH_Mesh*                    targetMesh,
6543                                  const bool                     topPresent)
6544 {
6545   PGroupIDs newGroupIDs( new list<int> );
6546   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6547
6548   // Sort existing groups by types and collect their names
6549
6550   // containers to store an old group and generated new ones;
6551   // 1st new group is for result elems of different type than a source one;
6552   // 2nd new group is for same type result elems ("top" group at extrusion)
6553   using boost::tuple;
6554   using boost::make_tuple;
6555   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6556   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6557   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6558   // group names
6559   set< string > groupNames;
6560
6561   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6562   if ( !groupIt->more() ) return newGroupIDs;
6563
6564   int newGroupID = mesh->GetGroupIds().back()+1;
6565   while ( groupIt->more() )
6566   {
6567     SMESH_Group * group = groupIt->next();
6568     if ( !group ) continue;
6569     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6570     if ( !groupDS || groupDS->IsEmpty() ) continue;
6571     groupNames.insert    ( group->GetName() );
6572     groupDS->SetStoreName( group->GetName() );
6573     const SMDSAbs_ElementType type = groupDS->GetType();
6574     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6575     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6576     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6577     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6578   }
6579
6580   // Loop on nodes and elements to add them in new groups
6581
6582   vector< const SMDS_MeshElement* > resultElems;
6583   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6584   {
6585     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6586     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6587     if ( gens.size() != elems.size() )
6588       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6589
6590     // loop on created elements
6591     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6592     {
6593       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6594       if ( !sourceElem ) {
6595         MESSAGE("generateGroups(): NULL source element");
6596         continue;
6597       }
6598       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6599       if ( groupsOldNew.empty() ) { // no groups of this type at all
6600         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6601           ++iElem; // skip all elements made by sourceElem
6602         continue;
6603       }
6604       // collect all elements made by the iElem-th sourceElem
6605       resultElems.clear();
6606       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6607         if ( resElem != sourceElem )
6608           resultElems.push_back( resElem );
6609       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6610         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6611           if ( resElem != sourceElem )
6612             resultElems.push_back( resElem );
6613
6614       const SMDS_MeshElement* topElem = 0;
6615       if ( isNodes ) // there must be a top element
6616       {
6617         topElem = resultElems.back();
6618         resultElems.pop_back();
6619       }
6620       else
6621       {
6622         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6623         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6624           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6625           {
6626             topElem = *resElemIt;
6627             *resElemIt = 0; // erase *resElemIt
6628             break;
6629           }
6630       }
6631       // add resultElems to groups originted from ones the sourceElem belongs to
6632       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6633       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6634       {
6635         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6636         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6637         {
6638           // fill in a new group
6639           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6640           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6641           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6642             if ( *resElemIt )
6643               newGroup.Add( *resElemIt );
6644
6645           // fill a "top" group
6646           if ( topElem )
6647           {
6648             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6649             newTopGroup.Add( topElem );
6650           }
6651         }
6652       }
6653     } // loop on created elements
6654   }// loop on nodes and elements
6655
6656   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6657
6658   list<int> topGrouIds;
6659   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6660   {
6661     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6662     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6663                                       orderedOldNewGroups[i]->get<2>() };
6664     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6665     {
6666       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6667       if ( newGroupDS->IsEmpty() )
6668       {
6669         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6670       }
6671       else
6672       {
6673         // set group type
6674         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6675
6676         // make a name
6677         const bool isTop = ( topPresent &&
6678                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6679                              is2nd );
6680
6681         string name = oldGroupDS->GetStoreName();
6682         { // remove trailing whitespaces (issue 22599)
6683           size_t size = name.size();
6684           while ( size > 1 && isspace( name[ size-1 ]))
6685             --size;
6686           if ( size != name.size() )
6687           {
6688             name.resize( size );
6689             oldGroupDS->SetStoreName( name.c_str() );
6690           }
6691         }
6692         if ( !targetMesh ) {
6693           string suffix = ( isTop ? "top": postfix.c_str() );
6694           name += "_";
6695           name += suffix;
6696           int nb = 1;
6697           while ( !groupNames.insert( name ).second ) // name exists
6698             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6699         }
6700         else if ( isTop ) {
6701           name += "_top";
6702         }
6703         newGroupDS->SetStoreName( name.c_str() );
6704
6705         // make a SMESH_Groups
6706         mesh->AddGroup( newGroupDS );
6707         if ( isTop )
6708           topGrouIds.push_back( newGroupDS->GetID() );
6709         else
6710           newGroupIDs->push_back( newGroupDS->GetID() );
6711       }
6712     }
6713   }
6714   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6715
6716   return newGroupIDs;
6717 }
6718
6719 //================================================================================
6720 /*!
6721  *  * \brief Return list of group of nodes close to each other within theTolerance
6722  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6723  *  *        an Octree algorithm
6724  *  \param [in,out] theNodes - the nodes to treat
6725  *  \param [in]     theTolerance - the tolerance
6726  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6727  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6728  *         corner and medium nodes in separate groups
6729  */
6730 //================================================================================
6731
6732 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6733                                             const double         theTolerance,
6734                                             TListOfListOfNodes & theGroupsOfNodes,
6735                                             bool                 theSeparateCornersAndMedium)
6736 {
6737   ClearLastCreated();
6738
6739   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6740        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6741        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6742     theSeparateCornersAndMedium = false;
6743
6744   TIDSortedNodeSet& corners = theNodes;
6745   TIDSortedNodeSet  medium;
6746
6747   if ( theNodes.empty() ) // get all nodes in the mesh
6748   {
6749     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6750     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6751     if ( theSeparateCornersAndMedium )
6752       while ( nIt->more() )
6753       {
6754         const SMDS_MeshNode* n = nIt->next();
6755         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6756         nodeSet->insert( nodeSet->end(), n );
6757       }
6758     else
6759       while ( nIt->more() )
6760         theNodes.insert( theNodes.end(), nIt->next() );
6761   }
6762   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6763   {
6764     TIDSortedNodeSet::iterator nIt = corners.begin();
6765     while ( nIt != corners.end() )
6766       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6767       {
6768         medium.insert( medium.end(), *nIt );
6769         corners.erase( nIt++ );
6770       }
6771       else
6772       {
6773         ++nIt;
6774       }
6775   }
6776
6777   if ( !corners.empty() )
6778     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6779   if ( !medium.empty() )
6780     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6781 }
6782
6783 //=======================================================================
6784 //function : SimplifyFace
6785 //purpose  : split a chain of nodes into several closed chains
6786 //=======================================================================
6787
6788 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6789                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6790                                     vector<int>&                         quantities) const
6791 {
6792   int nbNodes = faceNodes.size();
6793   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6794     --nbNodes;
6795   if ( nbNodes < 3 )
6796     return 0;
6797   size_t prevNbQuant = quantities.size();
6798
6799   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6800   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6801   map< const SMDS_MeshNode*, int >::iterator nInd;
6802
6803   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6804   simpleNodes.push_back( faceNodes[0] );
6805   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6806   {
6807     if ( faceNodes[ iCur ] != simpleNodes.back() )
6808     {
6809       int index = simpleNodes.size();
6810       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6811       int prevIndex = nInd->second;
6812       if ( prevIndex < index )
6813       {
6814         // a sub-loop found
6815         int loopLen = index - prevIndex;
6816         if ( loopLen > 2 )
6817         {
6818           // store the sub-loop
6819           quantities.push_back( loopLen );
6820           for ( int i = prevIndex; i < index; i++ )
6821             poly_nodes.push_back( simpleNodes[ i ]);
6822         }
6823         simpleNodes.resize( prevIndex+1 );
6824       }
6825       else
6826       {
6827         simpleNodes.push_back( faceNodes[ iCur ]);
6828       }
6829     }
6830   }
6831
6832   if ( simpleNodes.size() > 2 )
6833   {
6834     quantities.push_back( simpleNodes.size() );
6835     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6836   }
6837
6838   return quantities.size() - prevNbQuant;
6839 }
6840
6841 //=======================================================================
6842 //function : MergeNodes
6843 //purpose  : In each group, the cdr of nodes are substituted by the first one
6844 //           in all elements.
6845 //=======================================================================
6846
6847 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6848                                    const bool           theAvoidMakingHoles)
6849 {
6850   ClearLastCreated();
6851
6852   SMESHDS_Mesh* mesh = GetMeshDS();
6853
6854   TNodeNodeMap nodeNodeMap; // node to replace - new node
6855   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6856   list< smIdType > rmElemIds, rmNodeIds;
6857   vector< ElemFeatures > newElemDefs;
6858
6859   // Fill nodeNodeMap and elems
6860
6861   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6862   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6863   {
6864     list<const SMDS_MeshNode*>& nodes = *grIt;
6865     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6866     const SMDS_MeshNode* nToKeep = *nIt;
6867     for ( ++nIt; nIt != nodes.end(); nIt++ )
6868     {
6869       const SMDS_MeshNode* nToRemove = *nIt;
6870       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6871       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6872       while ( invElemIt->more() ) {
6873         const SMDS_MeshElement* elem = invElemIt->next();
6874         elems.insert(elem);
6875       }
6876     }
6877   }
6878
6879   // Apply recursive replacements (BUG 0020185)
6880   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6881   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6882   {
6883     const SMDS_MeshNode* nToKeep = nnIt->second;
6884     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6885     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6886     {
6887       nToKeep = nnIt_i->second;
6888       nnIt->second = nToKeep;
6889       nnIt_i = nodeNodeMap.find( nToKeep );
6890     }
6891   }
6892
6893   if ( theAvoidMakingHoles )
6894   {
6895     // find elements whose topology changes
6896
6897     vector<const SMDS_MeshElement*> pbElems;
6898     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6899     for ( ; eIt != elems.end(); ++eIt )
6900     {
6901       const SMDS_MeshElement* elem = *eIt;
6902       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6903       while ( itN->more() )
6904       {
6905         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6906         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6907         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6908         {
6909           // several nodes of elem stick
6910           pbElems.push_back( elem );
6911           break;
6912         }
6913       }
6914     }
6915     // exclude from merge nodes causing spoiling element
6916     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6917     {
6918       bool nodesExcluded = false;
6919       for ( size_t i = 0; i < pbElems.size(); ++i )
6920       {
6921         size_t prevNbMergeNodes = nodeNodeMap.size();
6922         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6923              prevNbMergeNodes < nodeNodeMap.size() )
6924           nodesExcluded = true;
6925       }
6926       if ( !nodesExcluded )
6927         break;
6928     }
6929   }
6930
6931   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6932   {
6933     const SMDS_MeshNode* nToRemove = nnIt->first;
6934     const SMDS_MeshNode* nToKeep   = nnIt->second;
6935     if ( nToRemove != nToKeep )
6936     {
6937       rmNodeIds.push_back( nToRemove->GetID() );
6938       AddToSameGroups( nToKeep, nToRemove, mesh );
6939       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6940       // w/o creating node in place of merged ones.
6941       SMDS_PositionPtr pos = nToRemove->GetPosition();
6942       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6943         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6944           sm->SetIsAlwaysComputed( true );
6945     }
6946   }
6947
6948   // Change element nodes or remove an element
6949
6950   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6951   for ( ; eIt != elems.end(); eIt++ )
6952   {
6953     const SMDS_MeshElement* elem = *eIt;
6954     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6955
6956     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6957     if ( !keepElem )
6958       rmElemIds.push_back( elem->GetID() );
6959
6960     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6961     {
6962       bool elemChanged = false;
6963       if ( i == 0 )
6964       {
6965         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
6966           elemChanged = mesh->ChangePolyhedronNodes( elem,
6967                                                      newElemDefs[i].myNodes,
6968                                                      newElemDefs[i].myPolyhedQuantities );
6969         else
6970           elemChanged = mesh->ChangeElementNodes( elem,
6971                                                   & newElemDefs[i].myNodes[0],
6972                                                   newElemDefs[i].myNodes.size() );
6973       }
6974       if ( i > 0 || !elemChanged )
6975       {
6976         if ( i == 0 )
6977         {
6978           newElemDefs[i].SetID( elem->GetID() );
6979           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6980           if ( !keepElem ) rmElemIds.pop_back();
6981         }
6982         else
6983         {
6984           newElemDefs[i].SetID( -1 );
6985         }
6986         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6987         if ( sm && newElem )
6988           sm->AddElement( newElem );
6989         if ( elem != newElem )
6990           ReplaceElemInGroups( elem, newElem, mesh );
6991       }
6992     }
6993   }
6994
6995   // Remove bad elements, then equal nodes (order important)
6996   Remove( rmElemIds, /*isNodes=*/false );
6997   Remove( rmNodeIds, /*isNodes=*/true );
6998
6999   return;
7000 }
7001
7002 //=======================================================================
7003 //function : applyMerge
7004 //purpose  : Compute new connectivity of an element after merging nodes
7005 //  \param [in] elems - the element
7006 //  \param [out] newElemDefs - definition(s) of result element(s)
7007 //  \param [inout] nodeNodeMap - nodes to merge
7008 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7009 //              after merging (but not degenerated), removes nodes causing
7010 //              the invalidity from \a nodeNodeMap.
7011 //  \return bool - true if the element should be removed
7012 //=======================================================================
7013
7014 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7015                                    vector< ElemFeatures >& newElemDefs,
7016                                    TNodeNodeMap&           nodeNodeMap,
7017                                    const bool              avoidMakingHoles )
7018 {
7019   bool toRemove = false; // to remove elem
7020   int nbResElems = 1;    // nb new elements
7021
7022   newElemDefs.resize(nbResElems);
7023   newElemDefs[0].Init( elem );
7024   newElemDefs[0].myNodes.clear();
7025
7026   set<const SMDS_MeshNode*> nodeSet;
7027   vector< const SMDS_MeshNode*>   curNodes;
7028   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7029   vector<int> iRepl;
7030
7031   const        int  nbNodes = elem->NbNodes();
7032   SMDSAbs_EntityType entity = elem->GetEntityType();
7033
7034   curNodes.resize( nbNodes );
7035   uniqueNodes.resize( nbNodes );
7036   iRepl.resize( nbNodes );
7037   int iUnique = 0, iCur = 0, nbRepl = 0;
7038
7039   // Get new seq of nodes
7040
7041   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7042   while ( itN->more() )
7043   {
7044     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7045
7046     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7047     if ( nnIt != nodeNodeMap.end() ) {
7048       n = (*nnIt).second;
7049     }
7050     curNodes[ iCur ] = n;
7051     bool isUnique = nodeSet.insert( n ).second;
7052     if ( isUnique )
7053       uniqueNodes[ iUnique++ ] = n;
7054     else
7055       iRepl[ nbRepl++ ] = iCur;
7056     iCur++;
7057   }
7058
7059   // Analyse element topology after replacement
7060
7061   int nbUniqueNodes = nodeSet.size();
7062   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7063   {
7064     toRemove = true;
7065     nbResElems = 0;
7066
7067     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7068     {
7069       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7070       int nbCorners = nbNodes / 2;
7071       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7072       {
7073         int iNext = ( iCur + 1 ) % nbCorners;
7074         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7075         {
7076           int iMedium = iCur + nbCorners;
7077           vector< const SMDS_MeshNode* >::iterator i =
7078             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7079                        uniqueNodes.end(),
7080                        curNodes[ iMedium ]);
7081           if ( i != uniqueNodes.end() )
7082           {
7083             --nbUniqueNodes;
7084             for ( ; i+1 != uniqueNodes.end(); ++i )
7085               *i = *(i+1);
7086           }
7087         }
7088       }
7089     }
7090
7091     switch ( entity )
7092     {
7093     case SMDSEntity_Polygon:
7094     case SMDSEntity_Quad_Polygon: // Polygon
7095     {
7096       ElemFeatures* elemType = & newElemDefs[0];
7097       const bool isQuad = elemType->myIsQuad;
7098       if ( isQuad )
7099         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7100           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7101
7102       // a polygon can divide into several elements
7103       vector<const SMDS_MeshNode *> polygons_nodes;
7104       vector<int> quantities;
7105       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7106       newElemDefs.resize( nbResElems );
7107       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7108       {
7109         ElemFeatures* elemType = & newElemDefs[iface];
7110         if ( iface ) elemType->Init( elem );
7111
7112         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7113         int nbNewNodes = quantities[iface];
7114         face_nodes.assign( polygons_nodes.begin() + inode,
7115                            polygons_nodes.begin() + inode + nbNewNodes );
7116         inode += nbNewNodes;
7117         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7118         {
7119           bool isValid = ( nbNewNodes % 2 == 0 );
7120           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7121             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7122           elemType->SetQuad( isValid );
7123           if ( isValid ) // put medium nodes after corners
7124             SMDS_MeshCell::applyInterlaceRev
7125               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7126                                                     nbNewNodes ), face_nodes );
7127         }
7128         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7129       }
7130       nbUniqueNodes = newElemDefs[0].myNodes.size();
7131       break;
7132     } // Polygon
7133
7134     case SMDSEntity_Polyhedra: // Polyhedral volume
7135     {
7136       if ( nbUniqueNodes >= 4 )
7137       {
7138         // each face has to be analyzed in order to check volume validity
7139         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7140         {
7141           toRemove = false;
7142           int nbFaces = aPolyedre->NbFaces();
7143
7144           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7145           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7146           vector<const SMDS_MeshNode *>  faceNodes;
7147           poly_nodes.clear();
7148           quantities.clear();
7149
7150           for (int iface = 1; iface <= nbFaces; iface++)
7151           {
7152             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7153             faceNodes.resize( nbFaceNodes );
7154             for (int inode = 1; inode <= nbFaceNodes; inode++)
7155             {
7156               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7157               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7158               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7159                 faceNode = (*nnIt).second;
7160               faceNodes[inode - 1] = faceNode;
7161             }
7162             SimplifyFace(faceNodes, poly_nodes, quantities);
7163           }
7164
7165           if ( quantities.size() > 3 )
7166           {
7167             // TODO: remove coincident faces
7168             nbResElems = 1;
7169             nbUniqueNodes = newElemDefs[0].myNodes.size();
7170           }
7171         }
7172       }
7173     }
7174     break;
7175
7176     // Regular elements
7177     // TODO not all the possible cases are solved. Find something more generic?
7178     case SMDSEntity_Edge: //////// EDGE
7179     case SMDSEntity_Triangle: //// TRIANGLE
7180     case SMDSEntity_Quad_Triangle:
7181     case SMDSEntity_Tetra:
7182     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7183     {
7184       break;
7185     }
7186     case SMDSEntity_Quad_Edge:
7187     {
7188       break;
7189     }
7190     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7191     {
7192       if ( nbUniqueNodes < 3 )
7193         toRemove = true;
7194       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7195         toRemove = true; // opposite nodes stick
7196       else
7197         toRemove = false;
7198       break;
7199     }
7200     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7201     {
7202       //   1    5    2
7203       //    +---+---+
7204       //    |       |
7205       //   4+       +6
7206       //    |       |
7207       //    +---+---+
7208       //   0    7    3
7209       if ( nbUniqueNodes == 6 &&
7210            iRepl[0] < 4       &&
7211            ( nbRepl == 1 || iRepl[1] >= 4 ))
7212       {
7213         toRemove = false;
7214       }
7215       break;
7216     }
7217     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7218     {
7219       //   1    5    2
7220       //    +---+---+
7221       //    |       |
7222       //   4+  8+   +6
7223       //    |       |
7224       //    +---+---+
7225       //   0    7    3
7226       if ( nbUniqueNodes == 7 &&
7227            iRepl[0] < 4       &&
7228            ( nbRepl == 1 || iRepl[1] != 8 ))
7229       {
7230         toRemove = false;
7231       }
7232       break;
7233     }
7234     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7235     {
7236       if ( nbUniqueNodes == 4 ) {
7237         // ---------------------------------> tetrahedron
7238         if ( curNodes[3] == curNodes[4] &&
7239              curNodes[3] == curNodes[5] ) {
7240           // top nodes stick
7241           toRemove = false;
7242         }
7243         else if ( curNodes[0] == curNodes[1] &&
7244                   curNodes[0] == curNodes[2] ) {
7245           // bottom nodes stick: set a top before
7246           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7247           uniqueNodes[ 0 ] = curNodes [ 5 ];
7248           uniqueNodes[ 1 ] = curNodes [ 4 ];
7249           uniqueNodes[ 2 ] = curNodes [ 3 ];
7250           toRemove = false;
7251         }
7252         else if (( curNodes[0] == curNodes[3] ) +
7253                  ( curNodes[1] == curNodes[4] ) +
7254                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7255           // a lateral face turns into a line
7256           toRemove = false;
7257         }
7258       }
7259       else if ( nbUniqueNodes == 5 ) {
7260         // PENTAHEDRON --------------------> pyramid
7261         if ( curNodes[0] == curNodes[3] )
7262         {
7263           uniqueNodes[ 0 ] = curNodes[ 1 ];
7264           uniqueNodes[ 1 ] = curNodes[ 4 ];
7265           uniqueNodes[ 2 ] = curNodes[ 5 ];
7266           uniqueNodes[ 3 ] = curNodes[ 2 ];
7267           uniqueNodes[ 4 ] = curNodes[ 0 ];
7268           toRemove = false;
7269         }
7270         if ( curNodes[1] == curNodes[4] )
7271         {
7272           uniqueNodes[ 0 ] = curNodes[ 0 ];
7273           uniqueNodes[ 1 ] = curNodes[ 2 ];
7274           uniqueNodes[ 2 ] = curNodes[ 5 ];
7275           uniqueNodes[ 3 ] = curNodes[ 3 ];
7276           uniqueNodes[ 4 ] = curNodes[ 1 ];
7277           toRemove = false;
7278         }
7279         if ( curNodes[2] == curNodes[5] )
7280         {
7281           uniqueNodes[ 0 ] = curNodes[ 0 ];
7282           uniqueNodes[ 1 ] = curNodes[ 3 ];
7283           uniqueNodes[ 2 ] = curNodes[ 4 ];
7284           uniqueNodes[ 3 ] = curNodes[ 1 ];
7285           uniqueNodes[ 4 ] = curNodes[ 2 ];
7286           toRemove = false;
7287         }
7288       }
7289       break;
7290     }
7291     case SMDSEntity_Hexa:
7292     {
7293       //////////////////////////////////// HEXAHEDRON
7294       SMDS_VolumeTool hexa (elem);
7295       hexa.SetExternalNormal();
7296       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7297         //////////////////////// HEX ---> tetrahedron
7298         for ( int iFace = 0; iFace < 6; iFace++ ) {
7299           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7300           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7301               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7302               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7303             // one face turns into a point ...
7304             int  pickInd = ind[ 0 ];
7305             int iOppFace = hexa.GetOppFaceIndex( iFace );
7306             ind = hexa.GetFaceNodesIndices( iOppFace );
7307             int nbStick = 0;
7308             uniqueNodes.clear();
7309             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7310               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7311                 nbStick++;
7312               else
7313                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7314             }
7315             if ( nbStick == 1 ) {
7316               // ... and the opposite one - into a triangle.
7317               // set a top node
7318               uniqueNodes.push_back( curNodes[ pickInd ]);
7319               toRemove = false;
7320             }
7321             break;
7322           }
7323         }
7324       }
7325       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7326         //////////////////////// HEX ---> prism
7327         int nbTria = 0, iTria[3];
7328         const int *ind; // indices of face nodes
7329         // look for triangular faces
7330         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7331           ind = hexa.GetFaceNodesIndices( iFace );
7332           TIDSortedNodeSet faceNodes;
7333           for ( iCur = 0; iCur < 4; iCur++ )
7334             faceNodes.insert( curNodes[ind[iCur]] );
7335           if ( faceNodes.size() == 3 )
7336             iTria[ nbTria++ ] = iFace;
7337         }
7338         // check if triangles are opposite
7339         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7340         {
7341           // set nodes of the bottom triangle
7342           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7343           vector<int> indB;
7344           for ( iCur = 0; iCur < 4; iCur++ )
7345             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7346               indB.push_back( ind[iCur] );
7347           if ( !hexa.IsForward() )
7348             std::swap( indB[0], indB[2] );
7349           for ( iCur = 0; iCur < 3; iCur++ )
7350             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7351           // set nodes of the top triangle
7352           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7353           for ( iCur = 0; iCur < 3; ++iCur )
7354             for ( int j = 0; j < 4; ++j )
7355               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7356               {
7357                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7358                 break;
7359               }
7360           toRemove = false;
7361           break;
7362         }
7363       }
7364       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7365         //////////////////// HEXAHEDRON ---> pyramid
7366         for ( int iFace = 0; iFace < 6; iFace++ ) {
7367           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7368           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7369               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7370               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7371             // one face turns into a point ...
7372             int iOppFace = hexa.GetOppFaceIndex( iFace );
7373             ind = hexa.GetFaceNodesIndices( iOppFace );
7374             uniqueNodes.clear();
7375             for ( iCur = 0; iCur < 4; iCur++ ) {
7376               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7377                 break;
7378               else
7379                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7380             }
7381             if ( uniqueNodes.size() == 4 ) {
7382               // ... and the opposite one is a quadrangle
7383               // set a top node
7384               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7385               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7386               toRemove = false;
7387             }
7388             break;
7389           }
7390         }
7391       }
7392
7393       if ( toRemove && nbUniqueNodes > 4 ) {
7394         ////////////////// HEXAHEDRON ---> polyhedron
7395         hexa.SetExternalNormal();
7396         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7397         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7398         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7399         quantities.reserve( 6 );     quantities.clear();
7400         for ( int iFace = 0; iFace < 6; iFace++ )
7401         {
7402           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7403           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7404                curNodes[ind[1]] == curNodes[ind[3]] )
7405           {
7406             quantities.clear();
7407             break; // opposite nodes stick
7408           }
7409           nodeSet.clear();
7410           for ( iCur = 0; iCur < 4; iCur++ )
7411           {
7412             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7413               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7414           }
7415           if ( nodeSet.size() < 3 )
7416             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7417           else
7418             quantities.push_back( nodeSet.size() );
7419         }
7420         if ( quantities.size() >= 4 )
7421         {
7422           nbResElems = 1;
7423           nbUniqueNodes = poly_nodes.size();
7424           newElemDefs[0].SetPoly(true);
7425         }
7426       }
7427       break;
7428     } // case HEXAHEDRON
7429
7430     default:
7431       toRemove = true;
7432
7433     } // switch ( entity )
7434
7435     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7436     {
7437       // erase from nodeNodeMap nodes whose merge spoils elem
7438       vector< const SMDS_MeshNode* > noMergeNodes;
7439       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7440       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7441         nodeNodeMap.erase( noMergeNodes[i] );
7442     }
7443     
7444   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7445
7446   uniqueNodes.resize( nbUniqueNodes );
7447
7448   if ( !toRemove && nbResElems == 0 )
7449     nbResElems = 1;
7450
7451   newElemDefs.resize( nbResElems );
7452
7453   return !toRemove;
7454 }
7455
7456
7457 // ========================================================
7458 // class   : ComparableElement
7459 // purpose : allow comparing elements basing on their nodes
7460 // ========================================================
7461
7462 class ComparableElement : public boost::container::flat_set< smIdType >
7463 {
7464   typedef boost::container::flat_set< smIdType >  int_set;
7465
7466   const SMDS_MeshElement* myElem;
7467   smIdType                mySumID;
7468   mutable int             myGroupID;
7469
7470 public:
7471
7472   ComparableElement( const SMDS_MeshElement* theElem ):
7473     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7474   {
7475     this->reserve( theElem->NbNodes() );
7476     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7477     {
7478       smIdType id = nodeIt->next()->GetID();
7479       mySumID += id;
7480       this->insert( id );
7481     }
7482   }
7483
7484   const SMDS_MeshElement* GetElem() const { return myElem; }
7485
7486   int& GroupID() const { return myGroupID; }
7487   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7488
7489   ComparableElement( const ComparableElement& theSource ) // move copy
7490     : int_set()
7491   {
7492     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7493     (int_set&) (*this ) = std::move( src );
7494     myElem    = src.myElem;
7495     mySumID   = src.mySumID;
7496     myGroupID = src.myGroupID;
7497   }
7498
7499   static int HashCode(const ComparableElement& se, int limit )
7500   {
7501     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7502   }
7503   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7504   {
7505     return ( se1 == se2 );
7506   }
7507
7508 };
7509
7510 //=======================================================================
7511 //function : FindEqualElements
7512 //purpose  : Return list of group of elements built on the same nodes.
7513 //           Search among theElements or in the whole mesh if theElements is empty
7514 //=======================================================================
7515
7516 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7517                                           TListOfListOfElementsID & theGroupsOfElementsID )
7518 {
7519   ClearLastCreated();
7520
7521   SMDS_ElemIteratorPtr elemIt;
7522   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7523   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7524
7525   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7526   typedef std::list<smIdType>                                     TGroupOfElems;
7527   TMapOfElements               mapOfElements;
7528   std::vector< TGroupOfElems > arrayOfGroups;
7529   TGroupOfElems                groupOfElems;
7530
7531   while ( elemIt->more() )
7532   {
7533     const SMDS_MeshElement* curElem = elemIt->next();
7534     if ( curElem->IsNull() )
7535       continue;
7536     ComparableElement      compElem = curElem;
7537     // check uniqueness
7538     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7539     if ( elemInSet.GetElem() != curElem ) // coincident elem
7540     {
7541       int& iG = elemInSet.GroupID();
7542       if ( iG < 0 )
7543       {
7544         iG = arrayOfGroups.size();
7545         arrayOfGroups.push_back( groupOfElems );
7546         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7547       }
7548       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7549     }
7550   }
7551
7552   groupOfElems.clear();
7553   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7554   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7555   {
7556     if ( groupIt->size() > 1 ) {
7557       //groupOfElems.sort(); -- theElements are sorted already
7558       theGroupsOfElementsID.emplace_back( *groupIt );
7559     }
7560   }
7561 }
7562
7563 //=======================================================================
7564 //function : MergeElements
7565 //purpose  : In each given group, substitute all elements by the first one.
7566 //=======================================================================
7567
7568 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7569 {
7570   ClearLastCreated();
7571
7572   typedef list<smIdType> TListOfIDs;
7573   TListOfIDs rmElemIds; // IDs of elems to remove
7574
7575   SMESHDS_Mesh* aMesh = GetMeshDS();
7576
7577   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7578   while ( groupsIt != theGroupsOfElementsID.end() ) {
7579     TListOfIDs& aGroupOfElemID = *groupsIt;
7580     aGroupOfElemID.sort();
7581     int elemIDToKeep = aGroupOfElemID.front();
7582     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7583     aGroupOfElemID.pop_front();
7584     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7585     while ( idIt != aGroupOfElemID.end() ) {
7586       int elemIDToRemove = *idIt;
7587       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7588       // add the kept element in groups of removed one (PAL15188)
7589       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7590       rmElemIds.push_back( elemIDToRemove );
7591       ++idIt;
7592     }
7593     ++groupsIt;
7594   }
7595
7596   Remove( rmElemIds, false );
7597 }
7598
7599 //=======================================================================
7600 //function : MergeEqualElements
7601 //purpose  : Remove all but one of elements built on the same nodes.
7602 //=======================================================================
7603
7604 void SMESH_MeshEditor::MergeEqualElements()
7605 {
7606   TIDSortedElemSet aMeshElements; /* empty input ==
7607                                      to merge equal elements in the whole mesh */
7608   TListOfListOfElementsID aGroupsOfElementsID;
7609   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7610   MergeElements( aGroupsOfElementsID );
7611 }
7612
7613 //=======================================================================
7614 //function : findAdjacentFace
7615 //purpose  :
7616 //=======================================================================
7617
7618 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7619                                                 const SMDS_MeshNode* n2,
7620                                                 const SMDS_MeshElement* elem)
7621 {
7622   TIDSortedElemSet elemSet, avoidSet;
7623   if ( elem )
7624     avoidSet.insert ( elem );
7625   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7626 }
7627
7628 //=======================================================================
7629 //function : findSegment
7630 //purpose  : Return a mesh segment by two nodes one of which can be medium
7631 //=======================================================================
7632
7633 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7634                                            const SMDS_MeshNode* n2)
7635 {
7636   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7637   while ( it->more() )
7638   {
7639     const SMDS_MeshElement* seg = it->next();
7640     if ( seg->GetNodeIndex( n2 ) >= 0 )
7641       return seg;
7642   }
7643   return 0;
7644 }
7645
7646 //=======================================================================
7647 //function : FindFreeBorder
7648 //purpose  :
7649 //=======================================================================
7650
7651 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7652
7653 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7654                                        const SMDS_MeshNode*             theSecondNode,
7655                                        const SMDS_MeshNode*             theLastNode,
7656                                        list< const SMDS_MeshNode* > &   theNodes,
7657                                        list< const SMDS_MeshElement* >& theFaces)
7658 {
7659   if ( !theFirstNode || !theSecondNode )
7660     return false;
7661   // find border face between theFirstNode and theSecondNode
7662   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7663   if ( !curElem )
7664     return false;
7665
7666   theFaces.push_back( curElem );
7667   theNodes.push_back( theFirstNode );
7668   theNodes.push_back( theSecondNode );
7669
7670   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7671   //TIDSortedElemSet foundElems;
7672   bool needTheLast = ( theLastNode != 0 );
7673
7674   vector<const SMDS_MeshNode*> nodes;
7675   
7676   while ( nStart != theLastNode ) {
7677     if ( nStart == theFirstNode )
7678       return !needTheLast;
7679
7680     // find all free border faces sharing nStart
7681
7682     list< const SMDS_MeshElement* > curElemList;
7683     list< const SMDS_MeshNode* >    nStartList;
7684     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7685     while ( invElemIt->more() ) {
7686       const SMDS_MeshElement* e = invElemIt->next();
7687       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7688       {
7689         // get nodes
7690         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7691                       SMDS_MeshElement::iterator() );
7692         nodes.push_back( nodes[ 0 ]);
7693
7694         // check 2 links
7695         int iNode = 0, nbNodes = nodes.size() - 1;
7696         for ( iNode = 0; iNode < nbNodes; iNode++ )
7697           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7698                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7699               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7700           {
7701             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7702             curElemList.push_back( e );
7703           }
7704       }
7705     }
7706     // analyse the found
7707
7708     int nbNewBorders = curElemList.size();
7709     if ( nbNewBorders == 0 ) {
7710       // no free border furthermore
7711       return !needTheLast;
7712     }
7713     else if ( nbNewBorders == 1 ) {
7714       // one more element found
7715       nIgnore = nStart;
7716       nStart = nStartList.front();
7717       curElem = curElemList.front();
7718       theFaces.push_back( curElem );
7719       theNodes.push_back( nStart );
7720     }
7721     else {
7722       // several continuations found
7723       list< const SMDS_MeshElement* >::iterator curElemIt;
7724       list< const SMDS_MeshNode* >::iterator nStartIt;
7725       // check if one of them reached the last node
7726       if ( needTheLast ) {
7727         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7728              curElemIt!= curElemList.end();
7729              curElemIt++, nStartIt++ )
7730           if ( *nStartIt == theLastNode ) {
7731             theFaces.push_back( *curElemIt );
7732             theNodes.push_back( *nStartIt );
7733             return true;
7734           }
7735       }
7736       // find the best free border by the continuations
7737       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7738       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7739       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7740            curElemIt!= curElemList.end();
7741            curElemIt++, nStartIt++ )
7742       {
7743         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7744         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7745         // find one more free border
7746         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7747           cNL->clear();
7748           cFL->clear();
7749         }
7750         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7751           // choice: clear a worse one
7752           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7753           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7754           contNodes[ iWorse ].clear();
7755           contFaces[ iWorse ].clear();
7756         }
7757       }
7758       if ( contNodes[0].empty() && contNodes[1].empty() )
7759         return false;
7760
7761       // push_back the best free border
7762       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7763       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7764       //theNodes.pop_back(); // remove nIgnore
7765       theNodes.pop_back(); // remove nStart
7766       //theFaces.pop_back(); // remove curElem
7767       theNodes.splice( theNodes.end(), *cNL );
7768       theFaces.splice( theFaces.end(), *cFL );
7769       return true;
7770
7771     } // several continuations found
7772   } // while ( nStart != theLastNode )
7773
7774   return true;
7775 }
7776
7777 //=======================================================================
7778 //function : CheckFreeBorderNodes
7779 //purpose  : Return true if the tree nodes are on a free border
7780 //=======================================================================
7781
7782 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7783                                             const SMDS_MeshNode* theNode2,
7784                                             const SMDS_MeshNode* theNode3)
7785 {
7786   list< const SMDS_MeshNode* > nodes;
7787   list< const SMDS_MeshElement* > faces;
7788   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7789 }
7790
7791 //=======================================================================
7792 //function : SewFreeBorder
7793 //purpose  :
7794 //warning  : for border-to-side sewing theSideSecondNode is considered as
7795 //           the last side node and theSideThirdNode is not used
7796 //=======================================================================
7797
7798 SMESH_MeshEditor::Sew_Error
7799 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7800                                  const SMDS_MeshNode* theBordSecondNode,
7801                                  const SMDS_MeshNode* theBordLastNode,
7802                                  const SMDS_MeshNode* theSideFirstNode,
7803                                  const SMDS_MeshNode* theSideSecondNode,
7804                                  const SMDS_MeshNode* theSideThirdNode,
7805                                  const bool           theSideIsFreeBorder,
7806                                  const bool           toCreatePolygons,
7807                                  const bool           toCreatePolyedrs)
7808 {
7809   ClearLastCreated();
7810
7811   Sew_Error aResult = SEW_OK;
7812
7813   // ====================================
7814   //    find side nodes and elements
7815   // ====================================
7816
7817   list< const SMDS_MeshNode* >    nSide[ 2 ];
7818   list< const SMDS_MeshElement* > eSide[ 2 ];
7819   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7820   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7821
7822   // Free border 1
7823   // --------------
7824   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7825                       nSide[0], eSide[0])) {
7826     MESSAGE(" Free Border 1 not found " );
7827     aResult = SEW_BORDER1_NOT_FOUND;
7828   }
7829   if (theSideIsFreeBorder) {
7830     // Free border 2
7831     // --------------
7832     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7833                         nSide[1], eSide[1])) {
7834       MESSAGE(" Free Border 2 not found " );
7835       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7836     }
7837   }
7838   if ( aResult != SEW_OK )
7839     return aResult;
7840
7841   if (!theSideIsFreeBorder) {
7842     // Side 2
7843     // --------------
7844
7845     // -------------------------------------------------------------------------
7846     // Algo:
7847     // 1. If nodes to merge are not coincident, move nodes of the free border
7848     //    from the coord sys defined by the direction from the first to last
7849     //    nodes of the border to the correspondent sys of the side 2
7850     // 2. On the side 2, find the links most co-directed with the correspondent
7851     //    links of the free border
7852     // -------------------------------------------------------------------------
7853
7854     // 1. Since sewing may break if there are volumes to split on the side 2,
7855     //    we won't move nodes but just compute new coordinates for them
7856     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7857     TNodeXYZMap nBordXYZ;
7858     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7859     list< const SMDS_MeshNode* >::iterator nBordIt;
7860
7861     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7862     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7863     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7864     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7865     double tol2 = 1.e-8;
7866     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7867     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7868       // Need node movement.
7869
7870       // find X and Z axes to create trsf
7871       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7872       gp_Vec X = Zs ^ Zb;
7873       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7874         // Zb || Zs
7875         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7876
7877       // coord systems
7878       gp_Ax3 toBordAx( Pb1, Zb, X );
7879       gp_Ax3 fromSideAx( Ps1, Zs, X );
7880       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7881       // set trsf
7882       gp_Trsf toBordSys, fromSide2Sys;
7883       toBordSys.SetTransformation( toBordAx );
7884       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7885       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7886
7887       // move
7888       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7889         const SMDS_MeshNode* n = *nBordIt;
7890         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7891         toBordSys.Transforms( xyz );
7892         fromSide2Sys.Transforms( xyz );
7893         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7894       }
7895     }
7896     else {
7897       // just insert nodes XYZ in the nBordXYZ map
7898       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7899         const SMDS_MeshNode* n = *nBordIt;
7900         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7901       }
7902     }
7903
7904     // 2. On the side 2, find the links most co-directed with the correspondent
7905     //    links of the free border
7906
7907     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7908     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7909     sideNodes.push_back( theSideFirstNode );
7910
7911     bool hasVolumes = false;
7912     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7913     set<long> foundSideLinkIDs, checkedLinkIDs;
7914     SMDS_VolumeTool volume;
7915     //const SMDS_MeshNode* faceNodes[ 4 ];
7916
7917     const SMDS_MeshNode*    sideNode;
7918     const SMDS_MeshElement* sideElem  = 0;
7919     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7920     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7921     nBordIt = bordNodes.begin();
7922     nBordIt++;
7923     // border node position and border link direction to compare with
7924     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7925     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7926     // choose next side node by link direction or by closeness to
7927     // the current border node:
7928     bool searchByDir = ( *nBordIt != theBordLastNode );
7929     do {
7930       // find the next node on the Side 2
7931       sideNode = 0;
7932       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7933       long linkID;
7934       checkedLinkIDs.clear();
7935       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7936
7937       // loop on inverse elements of current node (prevSideNode) on the Side 2
7938       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7939       while ( invElemIt->more() )
7940       {
7941         const SMDS_MeshElement* elem = invElemIt->next();
7942         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7943         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7944         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7945         bool isVolume = volume.Set( elem );
7946         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7947         if ( isVolume ) // --volume
7948           hasVolumes = true;
7949         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7950           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7951           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7952           while ( nIt->more() ) {
7953             nodes[ iNode ] = cast2Node( nIt->next() );
7954             if ( nodes[ iNode++ ] == prevSideNode )
7955               iPrevNode = iNode - 1;
7956           }
7957           // there are 2 links to check
7958           nbNodes = 2;
7959         }
7960         else // --edge
7961           continue;
7962         // loop on links, to be precise, on the second node of links
7963         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7964           const SMDS_MeshNode* n = nodes[ iNode ];
7965           if ( isVolume ) {
7966             if ( !volume.IsLinked( n, prevSideNode ))
7967               continue;
7968           }
7969           else {
7970             if ( iNode ) // a node before prevSideNode
7971               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7972             else         // a node after prevSideNode
7973               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7974           }
7975           // check if this link was already used
7976           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7977           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7978           if (!isJustChecked &&
7979               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7980           {
7981             // test a link geometrically
7982             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7983             bool linkIsBetter = false;
7984             double dot = 0.0, dist = 0.0;
7985             if ( searchByDir ) { // choose most co-directed link
7986               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7987               linkIsBetter = ( dot > maxDot );
7988             }
7989             else { // choose link with the node closest to bordPos
7990               dist = ( nextXYZ - bordPos ).SquareModulus();
7991               linkIsBetter = ( dist < minDist );
7992             }
7993             if ( linkIsBetter ) {
7994               maxDot = dot;
7995               minDist = dist;
7996               linkID = iLink;
7997               sideNode = n;
7998               sideElem = elem;
7999             }
8000           }
8001         }
8002       } // loop on inverse elements of prevSideNode
8003
8004       if ( !sideNode ) {
8005         MESSAGE(" Can't find path by links of the Side 2 ");
8006         return SEW_BAD_SIDE_NODES;
8007       }
8008       sideNodes.push_back( sideNode );
8009       sideElems.push_back( sideElem );
8010       foundSideLinkIDs.insert ( linkID );
8011       prevSideNode = sideNode;
8012
8013       if ( *nBordIt == theBordLastNode )
8014         searchByDir = false;
8015       else {
8016         // find the next border link to compare with
8017         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8018         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8019         // move to next border node if sideNode is before forward border node (bordPos)
8020         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8021           prevBordNode = *nBordIt;
8022           nBordIt++;
8023           bordPos = nBordXYZ[ *nBordIt ];
8024           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8025           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8026         }
8027       }
8028     }
8029     while ( sideNode != theSideSecondNode );
8030
8031     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8032       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8033       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8034     }
8035   } // end nodes search on the side 2
8036
8037   // ============================
8038   // sew the border to the side 2
8039   // ============================
8040
8041   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8042   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8043
8044   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8045   if ( toMergeConformal && toCreatePolygons )
8046   {
8047     // do not merge quadrangles if polygons are OK (IPAL0052824)
8048     eIt[0] = eSide[0].begin();
8049     eIt[1] = eSide[1].begin();
8050     bool allQuads[2] = { true, true };
8051     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8052       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8053         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8054     }
8055     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8056   }
8057
8058   TListOfListOfNodes nodeGroupsToMerge;
8059   if (( toMergeConformal ) ||
8060       ( theSideIsFreeBorder && !theSideThirdNode )) {
8061
8062     // all nodes are to be merged
8063
8064     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8065          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8066          nIt[0]++, nIt[1]++ )
8067     {
8068       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8069       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8070       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8071     }
8072   }
8073   else {
8074
8075     // insert new nodes into the border and the side to get equal nb of segments
8076
8077     // get normalized parameters of nodes on the borders
8078     vector< double > param[ 2 ];
8079     param[0].resize( maxNbNodes );
8080     param[1].resize( maxNbNodes );
8081     int iNode, iBord;
8082     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8083       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8084       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8085       const SMDS_MeshNode* nPrev = *nIt;
8086       double bordLength = 0;
8087       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8088         const SMDS_MeshNode* nCur = *nIt;
8089         gp_XYZ segment (nCur->X() - nPrev->X(),
8090                         nCur->Y() - nPrev->Y(),
8091                         nCur->Z() - nPrev->Z());
8092         double segmentLen = segment.Modulus();
8093         bordLength += segmentLen;
8094         param[ iBord ][ iNode ] = bordLength;
8095         nPrev = nCur;
8096       }
8097       // normalize within [0,1]
8098       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8099         param[ iBord ][ iNode ] /= bordLength;
8100       }
8101     }
8102
8103     // loop on border segments
8104     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8105     int i[ 2 ] = { 0, 0 };
8106     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8107     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8108
8109     // element can be split while iterating on border if it has two edges in the border
8110     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8111     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8112
8113     TElemOfNodeListMap insertMap;
8114     TElemOfNodeListMap::iterator insertMapIt;
8115     // insertMap is
8116     // key:   elem to insert nodes into
8117     // value: 2 nodes to insert between + nodes to be inserted
8118     do {
8119       bool next[ 2 ] = { false, false };
8120
8121       // find min adjacent segment length after sewing
8122       double nextParam = 10., prevParam = 0;
8123       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8124         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8125           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8126         if ( i[ iBord ] > 0 )
8127           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8128       }
8129       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8130       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8131       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8132
8133       // choose to insert or to merge nodes
8134       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8135       if ( Abs( du ) <= minSegLen * 0.2 ) {
8136         // merge
8137         // ------
8138         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8139         const SMDS_MeshNode* n0 = *nIt[0];
8140         const SMDS_MeshNode* n1 = *nIt[1];
8141         nodeGroupsToMerge.back().push_back( n1 );
8142         nodeGroupsToMerge.back().push_back( n0 );
8143         // position of node of the border changes due to merge
8144         param[ 0 ][ i[0] ] += du;
8145         // move n1 for the sake of elem shape evaluation during insertion.
8146         // n1 will be removed by MergeNodes() anyway
8147         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8148         next[0] = next[1] = true;
8149       }
8150       else {
8151         // insert
8152         // ------
8153         int intoBord = ( du < 0 ) ? 0 : 1;
8154         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8155         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8156         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8157         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8158         if ( intoBord == 1 ) {
8159           // move node of the border to be on a link of elem of the side
8160           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8161           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8162           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8163           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8164         }
8165         elemReplaceMapIt = elemReplaceMap.find( elem );
8166         if ( elemReplaceMapIt != elemReplaceMap.end() )
8167           elem = elemReplaceMapIt->second;
8168
8169         insertMapIt = insertMap.find( elem );
8170         bool  notFound = ( insertMapIt == insertMap.end() );
8171         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8172         if ( otherLink ) {
8173           // insert into another link of the same element:
8174           // 1. perform insertion into the other link of the elem
8175           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8176           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8177           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8178           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8179           // 2. perform insertion into the link of adjacent faces
8180           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8181             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8182           }
8183           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8184             InsertNodesIntoLink( seg, n12, n22, nodeList );
8185           }
8186           if (toCreatePolyedrs) {
8187             // perform insertion into the links of adjacent volumes
8188             UpdateVolumes(n12, n22, nodeList);
8189           }
8190           // 3. find an element appeared on n1 and n2 after the insertion
8191           insertMap.erase( insertMapIt );
8192           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8193           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8194           elem = elem2;
8195         }
8196         if ( notFound || otherLink ) {
8197           // add element and nodes of the side into the insertMap
8198           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8199           (*insertMapIt).second.push_back( n1 );
8200           (*insertMapIt).second.push_back( n2 );
8201         }
8202         // add node to be inserted into elem
8203         (*insertMapIt).second.push_back( nIns );
8204         next[ 1 - intoBord ] = true;
8205       }
8206
8207       // go to the next segment
8208       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8209         if ( next[ iBord ] ) {
8210           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8211             eIt[ iBord ]++;
8212           nPrev[ iBord ] = *nIt[ iBord ];
8213           nIt[ iBord ]++; i[ iBord ]++;
8214         }
8215       }
8216     }
8217     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8218
8219     // perform insertion of nodes into elements
8220
8221     for (insertMapIt = insertMap.begin();
8222          insertMapIt != insertMap.end();
8223          insertMapIt++ )
8224     {
8225       const SMDS_MeshElement* elem = (*insertMapIt).first;
8226       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8227       if ( nodeList.size() < 3 ) continue;
8228       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8229       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8230
8231       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8232
8233       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8234         InsertNodesIntoLink( seg, n1, n2, nodeList );
8235       }
8236
8237       if ( !theSideIsFreeBorder ) {
8238         // look for and insert nodes into the faces adjacent to elem
8239         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8240           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8241         }
8242       }
8243       if (toCreatePolyedrs) {
8244         // perform insertion into the links of adjacent volumes
8245         UpdateVolumes(n1, n2, nodeList);
8246       }
8247     }
8248   } // end: insert new nodes
8249
8250   MergeNodes ( nodeGroupsToMerge );
8251
8252
8253   // Remove coincident segments
8254
8255   // get new segments
8256   TIDSortedElemSet segments;
8257   SMESH_SequenceOfElemPtr newFaces;
8258   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8259   {
8260     if ( !myLastCreatedElems[i] ) continue;
8261     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8262       segments.insert( segments.end(), myLastCreatedElems[i] );
8263     else
8264       newFaces.push_back( myLastCreatedElems[i] );
8265   }
8266   // get segments adjacent to merged nodes
8267   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8268   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8269   {
8270     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8271     if ( nodes.front()->IsNull() ) continue;
8272     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8273     while ( segIt->more() )
8274       segments.insert( segIt->next() );
8275   }
8276
8277   // find coincident
8278   TListOfListOfElementsID equalGroups;
8279   if ( !segments.empty() )
8280     FindEqualElements( segments, equalGroups );
8281   if ( !equalGroups.empty() )
8282   {
8283     // remove from segments those that will be removed
8284     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8285     for ( ; itGroups != equalGroups.end(); ++itGroups )
8286     {
8287       list< smIdType >& group = *itGroups;
8288       list< smIdType >::iterator id = group.begin();
8289       for ( ++id; id != group.end(); ++id )
8290         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8291           segments.erase( seg );
8292     }
8293     // remove equal segments
8294     MergeElements( equalGroups );
8295
8296     // restore myLastCreatedElems
8297     myLastCreatedElems = newFaces;
8298     TIDSortedElemSet::iterator seg = segments.begin();
8299     for ( ; seg != segments.end(); ++seg )
8300       myLastCreatedElems.push_back( *seg );
8301   }
8302
8303   return aResult;
8304 }
8305
8306 //=======================================================================
8307 //function : InsertNodesIntoLink
8308 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8309 //           and theBetweenNode2 and split theElement
8310 //=======================================================================
8311
8312 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8313                                            const SMDS_MeshNode*        theBetweenNode1,
8314                                            const SMDS_MeshNode*        theBetweenNode2,
8315                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8316                                            const bool                  toCreatePoly)
8317 {
8318   if ( !theElement ) return;
8319
8320   SMESHDS_Mesh *aMesh = GetMeshDS();
8321   vector<const SMDS_MeshElement*> newElems;
8322
8323   if ( theElement->GetType() == SMDSAbs_Edge )
8324   {
8325     theNodesToInsert.push_front( theBetweenNode1 );
8326     theNodesToInsert.push_back ( theBetweenNode2 );
8327     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8328     const SMDS_MeshNode* n1 = *n;
8329     for ( ++n; n != theNodesToInsert.end(); ++n )
8330     {
8331       const SMDS_MeshNode* n2 = *n;
8332       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8333         AddToSameGroups( seg, theElement, aMesh );
8334       else
8335         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8336       n1 = n2;
8337     }
8338     theNodesToInsert.pop_front();
8339     theNodesToInsert.pop_back();
8340
8341     if ( theElement->IsQuadratic() ) // add a not split part
8342     {
8343       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8344                                           theElement->end_nodes() );
8345       int iOther = 0, nbN = nodes.size();
8346       for ( ; iOther < nbN; ++iOther )
8347         if ( nodes[iOther] != theBetweenNode1 &&
8348              nodes[iOther] != theBetweenNode2 )
8349           break;
8350       if      ( iOther == 0 )
8351       {
8352         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8353           AddToSameGroups( seg, theElement, aMesh );
8354         else
8355           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8356       }
8357       else if ( iOther == 2 )
8358       {
8359         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8360           AddToSameGroups( seg, theElement, aMesh );
8361         else
8362           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8363       }
8364     }
8365     // treat new elements
8366     for ( size_t i = 0; i < newElems.size(); ++i )
8367       if ( newElems[i] )
8368       {
8369         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8370         myLastCreatedElems.push_back( newElems[i] );
8371       }
8372     ReplaceElemInGroups( theElement, newElems, aMesh );
8373     aMesh->RemoveElement( theElement );
8374     return;
8375
8376   } // if ( theElement->GetType() == SMDSAbs_Edge )
8377
8378   const SMDS_MeshElement* theFace = theElement;
8379   if ( theFace->GetType() != SMDSAbs_Face ) return;
8380
8381   // find indices of 2 link nodes and of the rest nodes
8382   int iNode = 0, il1, il2, i3, i4;
8383   il1 = il2 = i3 = i4 = -1;
8384   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8385
8386   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8387   while ( nodeIt->more() ) {
8388     const SMDS_MeshNode* n = nodeIt->next();
8389     if ( n == theBetweenNode1 )
8390       il1 = iNode;
8391     else if ( n == theBetweenNode2 )
8392       il2 = iNode;
8393     else if ( i3 < 0 )
8394       i3 = iNode;
8395     else
8396       i4 = iNode;
8397     nodes[ iNode++ ] = n;
8398   }
8399   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8400     return ;
8401
8402   // arrange link nodes to go one after another regarding the face orientation
8403   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8404   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8405   if ( reverse ) {
8406     iNode = il1;
8407     il1 = il2;
8408     il2 = iNode;
8409     aNodesToInsert.reverse();
8410   }
8411   // check that not link nodes of a quadrangles are in good order
8412   int nbFaceNodes = theFace->NbNodes();
8413   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8414     iNode = i3;
8415     i3 = i4;
8416     i4 = iNode;
8417   }
8418
8419   if (toCreatePoly || theFace->IsPoly()) {
8420
8421     iNode = 0;
8422     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8423
8424     // add nodes of face up to first node of link
8425     bool isFLN = false;
8426     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8427     while ( nodeIt->more() && !isFLN ) {
8428       const SMDS_MeshNode* n = nodeIt->next();
8429       poly_nodes[iNode++] = n;
8430       isFLN = ( n == nodes[il1] );
8431     }
8432     // add nodes to insert
8433     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8434     for (; nIt != aNodesToInsert.end(); nIt++) {
8435       poly_nodes[iNode++] = *nIt;
8436     }
8437     // add nodes of face starting from last node of link
8438     while ( nodeIt->more() ) {
8439       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8440       poly_nodes[iNode++] = n;
8441     }
8442
8443     // make a new face
8444     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8445   }
8446
8447   else if ( !theFace->IsQuadratic() )
8448   {
8449     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8450     int nbLinkNodes = 2 + aNodesToInsert.size();
8451     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8452     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8453     linkNodes[ 0 ] = nodes[ il1 ];
8454     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8455     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8456     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8457       linkNodes[ iNode++ ] = *nIt;
8458     }
8459     // decide how to split a quadrangle: compare possible variants
8460     // and choose which of splits to be a quadrangle
8461     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8462     if ( nbFaceNodes == 3 ) {
8463       iBestQuad = nbSplits;
8464       i4 = i3;
8465     }
8466     else if ( nbFaceNodes == 4 ) {
8467       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8468       double aBestRate = DBL_MAX;
8469       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8470         i1 = 0; i2 = 1;
8471         double aBadRate = 0;
8472         // evaluate elements quality
8473         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8474           if ( iSplit == iQuad ) {
8475             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8476                                    linkNodes[ i2++ ],
8477                                    nodes[ i3 ],
8478                                    nodes[ i4 ]);
8479             aBadRate += getBadRate( &quad, aCrit );
8480           }
8481           else {
8482             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8483                                    linkNodes[ i2++ ],
8484                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8485             aBadRate += getBadRate( &tria, aCrit );
8486           }
8487         }
8488         // choice
8489         if ( aBadRate < aBestRate ) {
8490           iBestQuad = iQuad;
8491           aBestRate = aBadRate;
8492         }
8493       }
8494     }
8495
8496     // create new elements
8497     i1 = 0; i2 = 1;
8498     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8499     {
8500       if ( iSplit == iBestQuad )
8501         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8502                                             linkNodes[ i2++ ],
8503                                             nodes[ i3 ],
8504                                             nodes[ i4 ]));
8505       else
8506         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8507                                             linkNodes[ i2++ ],
8508                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8509     }
8510
8511     const SMDS_MeshNode* newNodes[ 4 ];
8512     newNodes[ 0 ] = linkNodes[ i1 ];
8513     newNodes[ 1 ] = linkNodes[ i2 ];
8514     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8515     newNodes[ 3 ] = nodes[ i4 ];
8516     if (iSplit == iBestQuad)
8517       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8518     else
8519       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8520
8521   } // end if(!theFace->IsQuadratic())
8522
8523   else { // theFace is quadratic
8524     // we have to split theFace on simple triangles and one simple quadrangle
8525     int tmp = il1/2;
8526     int nbshift = tmp*2;
8527     // shift nodes in nodes[] by nbshift
8528     int i,j;
8529     for(i=0; i<nbshift; i++) {
8530       const SMDS_MeshNode* n = nodes[0];
8531       for(j=0; j<nbFaceNodes-1; j++) {
8532         nodes[j] = nodes[j+1];
8533       }
8534       nodes[nbFaceNodes-1] = n;
8535     }
8536     il1 = il1 - nbshift;
8537     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8538     //   n0      n1     n2    n0      n1     n2
8539     //     +-----+-----+        +-----+-----+
8540     //      \         /         |           |
8541     //       \       /          |           |
8542     //      n5+     +n3       n7+           +n3
8543     //         \   /            |           |
8544     //          \ /             |           |
8545     //           +              +-----+-----+
8546     //           n4           n6      n5     n4
8547
8548     // create new elements
8549     int n1,n2,n3;
8550     if ( nbFaceNodes == 6 ) { // quadratic triangle
8551       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8552       if ( theFace->IsMediumNode(nodes[il1]) ) {
8553         // create quadrangle
8554         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8555         n1 = 1;
8556         n2 = 2;
8557         n3 = 3;
8558       }
8559       else {
8560         // create quadrangle
8561         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8562         n1 = 0;
8563         n2 = 1;
8564         n3 = 5;
8565       }
8566     }
8567     else { // nbFaceNodes==8 - quadratic quadrangle
8568       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8569       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8570       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8571       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8572         // create quadrangle
8573         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8574         n1 = 1;
8575         n2 = 2;
8576         n3 = 3;
8577       }
8578       else {
8579         // create quadrangle
8580         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8581         n1 = 0;
8582         n2 = 1;
8583         n3 = 7;
8584       }
8585     }
8586     // create needed triangles using n1,n2,n3 and inserted nodes
8587     int nbn = 2 + aNodesToInsert.size();
8588     vector<const SMDS_MeshNode*> aNodes(nbn);
8589     aNodes[0    ] = nodes[n1];
8590     aNodes[nbn-1] = nodes[n2];
8591     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8592     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8593       aNodes[iNode++] = *nIt;
8594     }
8595     for ( i = 1; i < nbn; i++ )
8596       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8597   }
8598
8599   // remove the old face
8600   for ( size_t i = 0; i < newElems.size(); ++i )
8601     if ( newElems[i] )
8602     {
8603       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8604       myLastCreatedElems.push_back( newElems[i] );
8605     }
8606   ReplaceElemInGroups( theFace, newElems, aMesh );
8607   aMesh->RemoveElement(theFace);
8608
8609 } // InsertNodesIntoLink()
8610
8611 //=======================================================================
8612 //function : UpdateVolumes
8613 //purpose  :
8614 //=======================================================================
8615
8616 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8617                                       const SMDS_MeshNode*        theBetweenNode2,
8618                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8619 {
8620   ClearLastCreated();
8621
8622   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8623   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8624     const SMDS_MeshElement* elem = invElemIt->next();
8625
8626     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8627     SMDS_VolumeTool aVolume (elem);
8628     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8629       continue;
8630
8631     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8632     int iface, nbFaces = aVolume.NbFaces();
8633     vector<const SMDS_MeshNode *> poly_nodes;
8634     vector<int> quantities (nbFaces);
8635
8636     for (iface = 0; iface < nbFaces; iface++) {
8637       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8638       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8639       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8640
8641       for (int inode = 0; inode < nbFaceNodes; inode++) {
8642         poly_nodes.push_back(faceNodes[inode]);
8643
8644         if (nbInserted == 0) {
8645           if (faceNodes[inode] == theBetweenNode1) {
8646             if (faceNodes[inode + 1] == theBetweenNode2) {
8647               nbInserted = theNodesToInsert.size();
8648
8649               // add nodes to insert
8650               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8651               for (; nIt != theNodesToInsert.end(); nIt++) {
8652                 poly_nodes.push_back(*nIt);
8653               }
8654             }
8655           }
8656           else if (faceNodes[inode] == theBetweenNode2) {
8657             if (faceNodes[inode + 1] == theBetweenNode1) {
8658               nbInserted = theNodesToInsert.size();
8659
8660               // add nodes to insert in reversed order
8661               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8662               nIt--;
8663               for (; nIt != theNodesToInsert.begin(); nIt--) {
8664                 poly_nodes.push_back(*nIt);
8665               }
8666               poly_nodes.push_back(*nIt);
8667             }
8668           }
8669           else {
8670           }
8671         }
8672       }
8673       quantities[iface] = nbFaceNodes + nbInserted;
8674     }
8675
8676     // Replace the volume
8677     SMESHDS_Mesh *aMesh = GetMeshDS();
8678
8679     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8680     {
8681       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8682       myLastCreatedElems.push_back( newElem );
8683       ReplaceElemInGroups( elem, newElem, aMesh );
8684     }
8685     aMesh->RemoveElement( elem );
8686   }
8687 }
8688
8689 namespace
8690 {
8691   //================================================================================
8692   /*!
8693    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8694    */
8695   //================================================================================
8696
8697   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8698                            vector<const SMDS_MeshNode *> & nodes,
8699                            vector<int> &                   nbNodeInFaces )
8700   {
8701     nodes.clear();
8702     nbNodeInFaces.clear();
8703     SMDS_VolumeTool vTool ( elem );
8704     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8705     {
8706       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8707       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8708       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8709     }
8710   }
8711 }
8712
8713 //=======================================================================
8714 /*!
8715  * \brief Convert elements contained in a sub-mesh to quadratic
8716  * \return int - nb of checked elements
8717  */
8718 //=======================================================================
8719
8720 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8721                                                   SMESH_MesherHelper& theHelper,
8722                                                   const bool          theForce3d)
8723 {
8724   //MESSAGE("convertElemToQuadratic");
8725   smIdType nbElem = 0;
8726   if( !theSm ) return nbElem;
8727
8728   vector<int> nbNodeInFaces;
8729   vector<const SMDS_MeshNode *> nodes;
8730   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8731   while(ElemItr->more())
8732   {
8733     nbElem++;
8734     const SMDS_MeshElement* elem = ElemItr->next();
8735     if( !elem ) continue;
8736
8737     // analyse a necessity of conversion
8738     const SMDSAbs_ElementType aType = elem->GetType();
8739     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8740       continue;
8741     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8742     bool hasCentralNodes = false;
8743     if ( elem->IsQuadratic() )
8744     {
8745       bool alreadyOK;
8746       switch ( aGeomType ) {
8747       case SMDSEntity_Quad_Triangle:
8748       case SMDSEntity_Quad_Quadrangle:
8749       case SMDSEntity_Quad_Hexa:
8750       case SMDSEntity_Quad_Penta:
8751         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8752
8753       case SMDSEntity_BiQuad_Triangle:
8754       case SMDSEntity_BiQuad_Quadrangle:
8755       case SMDSEntity_TriQuad_Hexa:
8756       case SMDSEntity_BiQuad_Penta:
8757         alreadyOK = theHelper.GetIsBiQuadratic();
8758         hasCentralNodes = true;
8759         break;
8760       default:
8761         alreadyOK = true;
8762       }
8763       // take into account already present medium nodes
8764       switch ( aType ) {
8765       case SMDSAbs_Volume:
8766         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8767       case SMDSAbs_Face:
8768         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8769       case SMDSAbs_Edge:
8770         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8771       default:;
8772       }
8773       if ( alreadyOK )
8774         continue;
8775     }
8776     // get elem data needed to re-create it
8777     //
8778     const smIdType id = elem->GetID();
8779     const int nbNodes = elem->NbCornerNodes();
8780     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8781     if ( aGeomType == SMDSEntity_Polyhedra )
8782       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8783     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8784       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8785
8786     // remove a linear element
8787     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8788
8789     // remove central nodes of biquadratic elements (biquad->quad conversion)
8790     if ( hasCentralNodes )
8791       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8792         if ( nodes[i]->NbInverseElements() == 0 )
8793           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8794
8795     const SMDS_MeshElement* NewElem = 0;
8796
8797     switch( aType )
8798     {
8799     case SMDSAbs_Edge :
8800     {
8801       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8802       break;
8803     }
8804     case SMDSAbs_Face :
8805     {
8806       switch(nbNodes)
8807       {
8808       case 3:
8809         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8810         break;
8811       case 4:
8812         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8813         break;
8814       default:
8815         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8816       }
8817       break;
8818     }
8819     case SMDSAbs_Volume :
8820     {
8821       switch( aGeomType )
8822       {
8823       case SMDSEntity_Tetra:
8824         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8825         break;
8826       case SMDSEntity_Pyramid:
8827         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8828         break;
8829       case SMDSEntity_Penta:
8830       case SMDSEntity_Quad_Penta:
8831       case SMDSEntity_BiQuad_Penta:
8832         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8833         break;
8834       case SMDSEntity_Hexa:
8835       case SMDSEntity_Quad_Hexa:
8836       case SMDSEntity_TriQuad_Hexa:
8837         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8838                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8839         break;
8840       case SMDSEntity_Hexagonal_Prism:
8841       default:
8842         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8843       }
8844       break;
8845     }
8846     default :
8847       continue;
8848     }
8849     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8850     if( NewElem && NewElem->getshapeId() < 1 )
8851       theSm->AddElement( NewElem );
8852   }
8853   return nbElem;
8854 }
8855 //=======================================================================
8856 //function : ConvertToQuadratic
8857 //purpose  :
8858 //=======================================================================
8859
8860 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8861 {
8862   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8863   SMESHDS_Mesh* meshDS = GetMeshDS();
8864
8865   SMESH_MesherHelper aHelper(*myMesh);
8866
8867   aHelper.SetIsQuadratic( true );
8868   aHelper.SetIsBiQuadratic( theToBiQuad );
8869   aHelper.SetElementsOnShape(true);
8870   aHelper.ToFixNodeParameters( true );
8871
8872   // convert elements assigned to sub-meshes
8873   smIdType nbCheckedElems = 0;
8874   if ( myMesh->HasShapeToMesh() )
8875   {
8876     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8877     {
8878       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8879       while ( smIt->more() ) {
8880         SMESH_subMesh* sm = smIt->next();
8881         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8882           aHelper.SetSubShape( sm->GetSubShape() );
8883           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8884         }
8885       }
8886     }
8887   }
8888
8889   // convert elements NOT assigned to sub-meshes
8890   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8891   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8892   {
8893     aHelper.SetElementsOnShape(false);
8894     SMESHDS_SubMesh *smDS = 0;
8895
8896     // convert edges
8897     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8898     while( aEdgeItr->more() )
8899     {
8900       const SMDS_MeshEdge* edge = aEdgeItr->next();
8901       if ( !edge->IsQuadratic() )
8902       {
8903         smIdType                  id = edge->GetID();
8904         const SMDS_MeshNode* n1 = edge->GetNode(0);
8905         const SMDS_MeshNode* n2 = edge->GetNode(1);
8906
8907         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8908
8909         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8910         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8911       }
8912       else
8913       {
8914         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8915       }
8916     }
8917
8918     // convert faces
8919     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8920     while( aFaceItr->more() )
8921     {
8922       const SMDS_MeshFace* face = aFaceItr->next();
8923       if ( !face ) continue;
8924       
8925       const SMDSAbs_EntityType type = face->GetEntityType();
8926       bool alreadyOK;
8927       switch( type )
8928       {
8929       case SMDSEntity_Quad_Triangle:
8930       case SMDSEntity_Quad_Quadrangle:
8931         alreadyOK = !theToBiQuad;
8932         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8933         break;
8934       case SMDSEntity_BiQuad_Triangle:
8935       case SMDSEntity_BiQuad_Quadrangle:
8936         alreadyOK = theToBiQuad;
8937         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8938         break;
8939       default: alreadyOK = false;
8940       }
8941       if ( alreadyOK )
8942         continue;
8943
8944       const smIdType id = face->GetID();
8945       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8946
8947       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8948
8949       SMDS_MeshFace * NewFace = 0;
8950       switch( type )
8951       {
8952       case SMDSEntity_Triangle:
8953       case SMDSEntity_Quad_Triangle:
8954       case SMDSEntity_BiQuad_Triangle:
8955         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8956         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8957           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8958         break;
8959
8960       case SMDSEntity_Quadrangle:
8961       case SMDSEntity_Quad_Quadrangle:
8962       case SMDSEntity_BiQuad_Quadrangle:
8963         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8964         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8965           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8966         break;
8967
8968       default:;
8969         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8970       }
8971       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8972     }
8973
8974     // convert volumes
8975     vector<int> nbNodeInFaces;
8976     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8977     while(aVolumeItr->more())
8978     {
8979       const SMDS_MeshVolume* volume = aVolumeItr->next();
8980       if ( !volume ) continue;
8981
8982       const SMDSAbs_EntityType type = volume->GetEntityType();
8983       if ( volume->IsQuadratic() )
8984       {
8985         bool alreadyOK;
8986         switch ( type )
8987         {
8988         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8989         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8990         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8991         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8992         default:                      alreadyOK = true;
8993         }
8994         if ( alreadyOK )
8995         {
8996           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8997           continue;
8998         }
8999       }
9000       const smIdType id = volume->GetID();
9001       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9002       if ( type == SMDSEntity_Polyhedra )
9003         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9004       else if ( type == SMDSEntity_Hexagonal_Prism )
9005         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9006
9007       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9008
9009       SMDS_MeshVolume * NewVolume = 0;
9010       switch ( type )
9011       {
9012       case SMDSEntity_Tetra:
9013         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9014         break;
9015       case SMDSEntity_Hexa:
9016       case SMDSEntity_Quad_Hexa:
9017       case SMDSEntity_TriQuad_Hexa:
9018         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9019                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9020         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9021           if ( nodes[i]->NbInverseElements() == 0 )
9022             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9023         break;
9024       case SMDSEntity_Pyramid:
9025         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9026                                       nodes[3], nodes[4], id, theForce3d);
9027         break;
9028       case SMDSEntity_Penta:
9029       case SMDSEntity_Quad_Penta:
9030       case SMDSEntity_BiQuad_Penta:
9031         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9032                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9033         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9034           if ( nodes[i]->NbInverseElements() == 0 )
9035             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9036         break;
9037       case SMDSEntity_Hexagonal_Prism:
9038       default:
9039         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9040       }
9041       ReplaceElemInGroups(volume, NewVolume, meshDS);
9042     }
9043   }
9044
9045   if ( !theForce3d )
9046   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9047     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9048     // aHelper.FixQuadraticElements(myError);
9049     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9050   }
9051 }
9052
9053 //================================================================================
9054 /*!
9055  * \brief Makes given elements quadratic
9056  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9057  *  \param theElements - elements to make quadratic
9058  */
9059 //================================================================================
9060
9061 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9062                                           TIDSortedElemSet& theElements,
9063                                           const bool        theToBiQuad)
9064 {
9065   if ( theElements.empty() ) return;
9066
9067   // we believe that all theElements are of the same type
9068   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9069
9070   // get all nodes shared by theElements
9071   TIDSortedNodeSet allNodes;
9072   TIDSortedElemSet::iterator eIt = theElements.begin();
9073   for ( ; eIt != theElements.end(); ++eIt )
9074     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9075
9076   // complete theElements with elements of lower dim whose all nodes are in allNodes
9077
9078   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9079   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9080   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9081   for ( ; nIt != allNodes.end(); ++nIt )
9082   {
9083     const SMDS_MeshNode* n = *nIt;
9084     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9085     while ( invIt->more() )
9086     {
9087       const SMDS_MeshElement*      e = invIt->next();
9088       const SMDSAbs_ElementType type = e->GetType();
9089       if ( e->IsQuadratic() )
9090       {
9091         quadAdjacentElems[ type ].insert( e );
9092
9093         bool alreadyOK;
9094         switch ( e->GetEntityType() ) {
9095         case SMDSEntity_Quad_Triangle:
9096         case SMDSEntity_Quad_Quadrangle:
9097         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9098         case SMDSEntity_BiQuad_Triangle:
9099         case SMDSEntity_BiQuad_Quadrangle:
9100         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9101         default:                           alreadyOK = true;
9102         }
9103         if ( alreadyOK )
9104           continue;
9105       }
9106       if ( type >= elemType )
9107         continue; // same type or more complex linear element
9108
9109       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9110         continue; // e is already checked
9111
9112       // check nodes
9113       bool allIn = true;
9114       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9115       while ( nodeIt->more() && allIn )
9116         allIn = allNodes.count( nodeIt->next() );
9117       if ( allIn )
9118         theElements.insert(e );
9119     }
9120   }
9121
9122   SMESH_MesherHelper helper(*myMesh);
9123   helper.SetIsQuadratic( true );
9124   helper.SetIsBiQuadratic( theToBiQuad );
9125
9126   // add links of quadratic adjacent elements to the helper
9127
9128   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9129     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9130           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9131     {
9132       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9133     }
9134   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9135     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9136           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9137     {
9138       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9139     }
9140   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9141     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9142           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9143     {
9144       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9145     }
9146
9147   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9148
9149   SMESHDS_Mesh*  meshDS = GetMeshDS();
9150   SMESHDS_SubMesh* smDS = 0;
9151   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9152   {
9153     const SMDS_MeshElement* elem = *eIt;
9154
9155     bool alreadyOK;
9156     int nbCentralNodes = 0;
9157     switch ( elem->GetEntityType() ) {
9158       // linear convertible
9159     case SMDSEntity_Edge:
9160     case SMDSEntity_Triangle:
9161     case SMDSEntity_Quadrangle:
9162     case SMDSEntity_Tetra:
9163     case SMDSEntity_Pyramid:
9164     case SMDSEntity_Hexa:
9165     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9166       // quadratic that can become bi-quadratic
9167     case SMDSEntity_Quad_Triangle:
9168     case SMDSEntity_Quad_Quadrangle:
9169     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9170       // bi-quadratic
9171     case SMDSEntity_BiQuad_Triangle:
9172     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9173     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9174       // the rest
9175     default:                           alreadyOK = true;
9176     }
9177     if ( alreadyOK ) continue;
9178
9179     const SMDSAbs_ElementType type = elem->GetType();
9180     const smIdType              id = elem->GetID();
9181     const int              nbNodes = elem->NbCornerNodes();
9182     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9183
9184     helper.SetSubShape( elem->getshapeId() );
9185
9186     if ( !smDS || !smDS->Contains( elem ))
9187       smDS = meshDS->MeshElements( elem->getshapeId() );
9188     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9189
9190     SMDS_MeshElement * newElem = 0;
9191     switch( nbNodes )
9192     {
9193     case 4: // cases for most frequently used element types go first (for optimization)
9194       if ( type == SMDSAbs_Volume )
9195         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9196       else
9197         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9198       break;
9199     case 8:
9200       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9201                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9202       break;
9203     case 3:
9204       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9205       break;
9206     case 2:
9207       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9208       break;
9209     case 5:
9210       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9211                                  nodes[4], id, theForce3d);
9212       break;
9213     case 6:
9214       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9215                                  nodes[4], nodes[5], id, theForce3d);
9216       break;
9217     default:;
9218     }
9219     ReplaceElemInGroups( elem, newElem, meshDS);
9220     if( newElem && smDS )
9221       smDS->AddElement( newElem );
9222
9223     // remove central nodes
9224     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9225       if ( nodes[i]->NbInverseElements() == 0 )
9226         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9227
9228   } // loop on theElements
9229
9230   if ( !theForce3d )
9231   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9232     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9233     // helper.FixQuadraticElements( myError );
9234     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9235   }
9236 }
9237
9238 //=======================================================================
9239 /*!
9240  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9241  * \return smIdType - nb of checked elements
9242  */
9243 //=======================================================================
9244
9245 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9246                                           SMDS_ElemIteratorPtr theItr,
9247                                           const int            /*theShapeID*/)
9248 {
9249   smIdType nbElem = 0;
9250   SMESHDS_Mesh* meshDS = GetMeshDS();
9251   ElemFeatures elemType;
9252   vector<const SMDS_MeshNode *> nodes;
9253
9254   while( theItr->more() )
9255   {
9256     const SMDS_MeshElement* elem = theItr->next();
9257     nbElem++;
9258     if( elem && elem->IsQuadratic())
9259     {
9260       // get elem data
9261       int nbCornerNodes = elem->NbCornerNodes();
9262       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9263
9264       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9265
9266       //remove a quadratic element
9267       if ( !theSm || !theSm->Contains( elem ))
9268         theSm = meshDS->MeshElements( elem->getshapeId() );
9269       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9270
9271       // remove medium nodes
9272       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9273         if ( nodes[i]->NbInverseElements() == 0 )
9274           meshDS->RemoveFreeNode( nodes[i], theSm );
9275
9276       // add a linear element
9277       nodes.resize( nbCornerNodes );
9278       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9279       ReplaceElemInGroups(elem, newElem, meshDS);
9280       if( theSm && newElem )
9281         theSm->AddElement( newElem );
9282     }
9283   }
9284   return nbElem;
9285 }
9286
9287 //=======================================================================
9288 //function : ConvertFromQuadratic
9289 //purpose  :
9290 //=======================================================================
9291
9292 bool SMESH_MeshEditor::ConvertFromQuadratic()
9293 {
9294   smIdType nbCheckedElems = 0;
9295   if ( myMesh->HasShapeToMesh() )
9296   {
9297     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9298     {
9299       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9300       while ( smIt->more() ) {
9301         SMESH_subMesh* sm = smIt->next();
9302         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9303           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9304       }
9305     }
9306   }
9307
9308   smIdType totalNbElems =
9309     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9310   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9311   {
9312     SMESHDS_SubMesh *aSM = 0;
9313     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9314   }
9315
9316   return true;
9317 }
9318
9319 namespace
9320 {
9321   //================================================================================
9322   /*!
9323    * \brief Return true if all medium nodes of the element are in the node set
9324    */
9325   //================================================================================
9326
9327   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9328   {
9329     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9330       if ( !nodeSet.count( elem->GetNode(i) ))
9331         return false;
9332     return true;
9333   }
9334 }
9335
9336 //================================================================================
9337 /*!
9338  * \brief Makes given elements linear
9339  */
9340 //================================================================================
9341
9342 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9343 {
9344   if ( theElements.empty() ) return;
9345
9346   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9347   set<smIdType> mediumNodeIDs;
9348   TIDSortedElemSet::iterator eIt = theElements.begin();
9349   for ( ; eIt != theElements.end(); ++eIt )
9350   {
9351     const SMDS_MeshElement* e = *eIt;
9352     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9353       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9354   }
9355
9356   // replace given elements by linear ones
9357   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9358   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9359
9360   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9361   // except those elements sharing medium nodes of quadratic element whose medium nodes
9362   // are not all in mediumNodeIDs
9363
9364   // get remaining medium nodes
9365   TIDSortedNodeSet mediumNodes;
9366   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9367   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9368     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9369       mediumNodes.insert( mediumNodes.end(), n );
9370
9371   // find more quadratic elements to convert
9372   TIDSortedElemSet moreElemsToConvert;
9373   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9374   for ( ; nIt != mediumNodes.end(); ++nIt )
9375   {
9376     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9377     while ( invIt->more() )
9378     {
9379       const SMDS_MeshElement* e = invIt->next();
9380       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9381       {
9382         // find a more complex element including e and
9383         // whose medium nodes are not in mediumNodes
9384         bool complexFound = false;
9385         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9386         {
9387           SMDS_ElemIteratorPtr invIt2 =
9388             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9389           while ( invIt2->more() )
9390           {
9391             const SMDS_MeshElement* eComplex = invIt2->next();
9392             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9393             {
9394               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9395               if ( nbCommonNodes == e->NbNodes())
9396               {
9397                 complexFound = true;
9398                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9399                 break;
9400               }
9401             }
9402           }
9403         }
9404         if ( !complexFound )
9405           moreElemsToConvert.insert( e );
9406       }
9407     }
9408   }
9409   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9410   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9411 }
9412
9413 //=======================================================================
9414 //function : SewSideElements
9415 //purpose  :
9416 //=======================================================================
9417
9418 SMESH_MeshEditor::Sew_Error
9419 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9420                                    TIDSortedElemSet&    theSide2,
9421                                    const SMDS_MeshNode* theFirstNode1,
9422                                    const SMDS_MeshNode* theFirstNode2,
9423                                    const SMDS_MeshNode* theSecondNode1,
9424                                    const SMDS_MeshNode* theSecondNode2)
9425 {
9426   ClearLastCreated();
9427
9428   if ( theSide1.size() != theSide2.size() )
9429     return SEW_DIFF_NB_OF_ELEMENTS;
9430
9431   Sew_Error aResult = SEW_OK;
9432   // Algo:
9433   // 1. Build set of faces representing each side
9434   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9435   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9436
9437   // =======================================================================
9438   // 1. Build set of faces representing each side:
9439   // =======================================================================
9440   // a. build set of nodes belonging to faces
9441   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9442   // c. create temporary faces representing side of volumes if correspondent
9443   //    face does not exist
9444
9445   SMESHDS_Mesh* aMesh = GetMeshDS();
9446   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9447   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9448   TIDSortedElemSet             faceSet1, faceSet2;
9449   set<const SMDS_MeshElement*> volSet1,  volSet2;
9450   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9451   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9452   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9453   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9454   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9455   int iSide, iFace, iNode;
9456
9457   list<const SMDS_MeshElement* > tempFaceList;
9458   for ( iSide = 0; iSide < 2; iSide++ ) {
9459     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9460     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9461     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9462     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9463     set<const SMDS_MeshElement*>::iterator vIt;
9464     TIDSortedElemSet::iterator eIt;
9465     set<const SMDS_MeshNode*>::iterator    nIt;
9466
9467     // check that given nodes belong to given elements
9468     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9469     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9470     int firstIndex = -1, secondIndex = -1;
9471     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9472       const SMDS_MeshElement* elem = *eIt;
9473       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9474       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9475       if ( firstIndex > -1 && secondIndex > -1 ) break;
9476     }
9477     if ( firstIndex < 0 || secondIndex < 0 ) {
9478       // we can simply return until temporary faces created
9479       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9480     }
9481
9482     // -----------------------------------------------------------
9483     // 1a. Collect nodes of existing faces
9484     //     and build set of face nodes in order to detect missing
9485     //     faces corresponding to sides of volumes
9486     // -----------------------------------------------------------
9487
9488     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9489
9490     // loop on the given element of a side
9491     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9492       //const SMDS_MeshElement* elem = *eIt;
9493       const SMDS_MeshElement* elem = *eIt;
9494       if ( elem->GetType() == SMDSAbs_Face ) {
9495         faceSet->insert( elem );
9496         set <const SMDS_MeshNode*> faceNodeSet;
9497         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9498         while ( nodeIt->more() ) {
9499           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9500           nodeSet->insert( n );
9501           faceNodeSet.insert( n );
9502         }
9503         setOfFaceNodeSet.insert( faceNodeSet );
9504       }
9505       else if ( elem->GetType() == SMDSAbs_Volume )
9506         volSet->insert( elem );
9507     }
9508     // ------------------------------------------------------------------------------
9509     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9510     // ------------------------------------------------------------------------------
9511
9512     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9513       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9514       while ( fIt->more() ) { // loop on faces sharing a node
9515         const SMDS_MeshElement* f = fIt->next();
9516         if ( faceSet->find( f ) == faceSet->end() ) {
9517           // check if all nodes are in nodeSet and
9518           // complete setOfFaceNodeSet if they are
9519           set <const SMDS_MeshNode*> faceNodeSet;
9520           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9521           bool allInSet = true;
9522           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9523             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9524             if ( nodeSet->find( n ) == nodeSet->end() )
9525               allInSet = false;
9526             else
9527               faceNodeSet.insert( n );
9528           }
9529           if ( allInSet ) {
9530             faceSet->insert( f );
9531             setOfFaceNodeSet.insert( faceNodeSet );
9532           }
9533         }
9534       }
9535     }
9536
9537     // -------------------------------------------------------------------------
9538     // 1c. Create temporary faces representing sides of volumes if correspondent
9539     //     face does not exist
9540     // -------------------------------------------------------------------------
9541
9542     if ( !volSet->empty() ) {
9543       //int nodeSetSize = nodeSet->size();
9544
9545       // loop on given volumes
9546       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9547         SMDS_VolumeTool vol (*vIt);
9548         // loop on volume faces: find free faces
9549         // --------------------------------------
9550         list<const SMDS_MeshElement* > freeFaceList;
9551         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9552           if ( !vol.IsFreeFace( iFace ))
9553             continue;
9554           // check if there is already a face with same nodes in a face set
9555           const SMDS_MeshElement* aFreeFace = 0;
9556           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9557           int nbNodes = vol.NbFaceNodes( iFace );
9558           set <const SMDS_MeshNode*> faceNodeSet;
9559           vol.GetFaceNodes( iFace, faceNodeSet );
9560           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9561           if ( isNewFace ) {
9562             // no such a face is given but it still can exist, check it
9563             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9564             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9565           }
9566           if ( !aFreeFace ) {
9567             // create a temporary face
9568             if ( nbNodes == 3 ) {
9569               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9570               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9571             }
9572             else if ( nbNodes == 4 ) {
9573               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9574               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9575             }
9576             else {
9577               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9578               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9579               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9580             }
9581             if ( aFreeFace )
9582               tempFaceList.push_back( aFreeFace );
9583           }
9584
9585           if ( aFreeFace )
9586             freeFaceList.push_back( aFreeFace );
9587
9588         } // loop on faces of a volume
9589
9590         // choose one of several free faces of a volume
9591         // --------------------------------------------
9592         if ( freeFaceList.size() > 1 ) {
9593           // choose a face having max nb of nodes shared by other elems of a side
9594           int maxNbNodes = -1;
9595           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9596           while ( fIt != freeFaceList.end() ) { // loop on free faces
9597             int nbSharedNodes = 0;
9598             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9599             while ( nodeIt->more() ) { // loop on free face nodes
9600               const SMDS_MeshNode* n =
9601                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9602               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9603               while ( invElemIt->more() ) {
9604                 const SMDS_MeshElement* e = invElemIt->next();
9605                 nbSharedNodes += faceSet->count( e );
9606                 nbSharedNodes += elemSet->count( e );
9607               }
9608             }
9609             if ( nbSharedNodes > maxNbNodes ) {
9610               maxNbNodes = nbSharedNodes;
9611               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9612             }
9613             else if ( nbSharedNodes == maxNbNodes ) {
9614               fIt++;
9615             }
9616             else {
9617               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9618             }
9619           }
9620           if ( freeFaceList.size() > 1 )
9621           {
9622             // could not choose one face, use another way
9623             // choose a face most close to the bary center of the opposite side
9624             gp_XYZ aBC( 0., 0., 0. );
9625             set <const SMDS_MeshNode*> addedNodes;
9626             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9627             eIt = elemSet2->begin();
9628             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9629               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9630               while ( nodeIt->more() ) { // loop on free face nodes
9631                 const SMDS_MeshNode* n =
9632                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9633                 if ( addedNodes.insert( n ).second )
9634                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9635               }
9636             }
9637             aBC /= addedNodes.size();
9638             double minDist = DBL_MAX;
9639             fIt = freeFaceList.begin();
9640             while ( fIt != freeFaceList.end() ) { // loop on free faces
9641               double dist = 0;
9642               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9643               while ( nodeIt->more() ) { // loop on free face nodes
9644                 const SMDS_MeshNode* n =
9645                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9646                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9647                 dist += ( aBC - p ).SquareModulus();
9648               }
9649               if ( dist < minDist ) {
9650                 minDist = dist;
9651                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9652               }
9653               else
9654                 fIt = freeFaceList.erase( fIt++ );
9655             }
9656           }
9657         } // choose one of several free faces of a volume
9658
9659         if ( freeFaceList.size() == 1 ) {
9660           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9661           faceSet->insert( aFreeFace );
9662           // complete a node set with nodes of a found free face
9663           //           for ( iNode = 0; iNode < ; iNode++ )
9664           //             nodeSet->insert( fNodes[ iNode ] );
9665         }
9666
9667       } // loop on volumes of a side
9668
9669       //       // complete a set of faces if new nodes in a nodeSet appeared
9670       //       // ----------------------------------------------------------
9671       //       if ( nodeSetSize != nodeSet->size() ) {
9672       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9673       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9674       //           while ( fIt->more() ) { // loop on faces sharing a node
9675       //             const SMDS_MeshElement* f = fIt->next();
9676       //             if ( faceSet->find( f ) == faceSet->end() ) {
9677       //               // check if all nodes are in nodeSet and
9678       //               // complete setOfFaceNodeSet if they are
9679       //               set <const SMDS_MeshNode*> faceNodeSet;
9680       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9681       //               bool allInSet = true;
9682       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9683       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9684       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9685       //                   allInSet = false;
9686       //                 else
9687       //                   faceNodeSet.insert( n );
9688       //               }
9689       //               if ( allInSet ) {
9690       //                 faceSet->insert( f );
9691       //                 setOfFaceNodeSet.insert( faceNodeSet );
9692       //               }
9693       //             }
9694       //           }
9695       //         }
9696       //       }
9697     } // Create temporary faces, if there are volumes given
9698   } // loop on sides
9699
9700   if ( faceSet1.size() != faceSet2.size() ) {
9701     // delete temporary faces: they are in reverseElements of actual nodes
9702     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9703     //    while ( tmpFaceIt->more() )
9704     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9705     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9706     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9707     //      aMesh->RemoveElement(*tmpFaceIt);
9708     MESSAGE("Diff nb of faces");
9709     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9710   }
9711
9712   // ============================================================
9713   // 2. Find nodes to merge:
9714   //              bind a node to remove to a node to put instead
9715   // ============================================================
9716
9717   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9718   if ( theFirstNode1 != theFirstNode2 )
9719     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9720   if ( theSecondNode1 != theSecondNode2 )
9721     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9722
9723   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9724   set< long > linkIdSet; // links to process
9725   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9726
9727   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9728   list< NLink > linkList[2];
9729   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9730   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9731   // loop on links in linkList; find faces by links and append links
9732   // of the found faces to linkList
9733   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9734   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9735   {
9736     NLink link[] = { *linkIt[0], *linkIt[1] };
9737     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9738     if ( !linkIdSet.count( linkID ) )
9739       continue;
9740
9741     // by links, find faces in the face sets,
9742     // and find indices of link nodes in the found faces;
9743     // in a face set, there is only one or no face sharing a link
9744     // ---------------------------------------------------------------
9745
9746     const SMDS_MeshElement* face[] = { 0, 0 };
9747     vector<const SMDS_MeshNode*> fnodes[2];
9748     int iLinkNode[2][2];
9749     TIDSortedElemSet avoidSet;
9750     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9751       const SMDS_MeshNode* n1 = link[iSide].first;
9752       const SMDS_MeshNode* n2 = link[iSide].second;
9753       //cout << "Side " << iSide << " ";
9754       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9755       // find a face by two link nodes
9756       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9757                                                       *faceSetPtr[ iSide ], avoidSet,
9758                                                       &iLinkNode[iSide][0],
9759                                                       &iLinkNode[iSide][1] );
9760       if ( face[ iSide ])
9761       {
9762         //cout << " F " << face[ iSide]->GetID() <<endl;
9763         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9764         // put face nodes to fnodes
9765         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9766         fnodes[ iSide ].assign( nIt, nEnd );
9767         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9768       }
9769     }
9770
9771     // check similarity of elements of the sides
9772     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9773       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9774       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9775         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9776       }
9777       else {
9778         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9779       }
9780       break; // do not return because it's necessary to remove tmp faces
9781     }
9782
9783     // set nodes to merge
9784     // -------------------
9785
9786     if ( face[0] && face[1] )  {
9787       const int nbNodes = face[0]->NbNodes();
9788       if ( nbNodes != face[1]->NbNodes() ) {
9789         MESSAGE("Diff nb of face nodes");
9790         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9791         break; // do not return because it s necessary to remove tmp faces
9792       }
9793       bool reverse[] = { false, false }; // order of nodes in the link
9794       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9795         // analyse link orientation in faces
9796         int i1 = iLinkNode[ iSide ][ 0 ];
9797         int i2 = iLinkNode[ iSide ][ 1 ];
9798         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9799       }
9800       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9801       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9802       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9803       {
9804         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9805                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9806       }
9807
9808       // add other links of the faces to linkList
9809       // -----------------------------------------
9810
9811       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9812         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9813         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9814         if ( !iter_isnew.second ) { // already in a set: no need to process
9815           linkIdSet.erase( iter_isnew.first );
9816         }
9817         else // new in set == encountered for the first time: add
9818         {
9819           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9820           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9821           linkList[0].push_back ( NLink( n1, n2 ));
9822           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9823         }
9824       }
9825     } // 2 faces found
9826
9827     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9828       break;
9829
9830   } // loop on link lists
9831
9832   if ( aResult == SEW_OK &&
9833        ( //linkIt[0] != linkList[0].end() ||
9834         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9835     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9836              " " << (faceSetPtr[1]->empty()));
9837     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9838   }
9839
9840   // ====================================================================
9841   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9842   // ====================================================================
9843
9844   // delete temporary faces
9845   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9846   //  while ( tmpFaceIt->more() )
9847   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9848   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9849   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9850     aMesh->RemoveElement(*tmpFaceIt);
9851
9852   if ( aResult != SEW_OK)
9853     return aResult;
9854
9855   list< smIdType > nodeIDsToRemove;
9856   vector< const SMDS_MeshNode*> nodes;
9857   ElemFeatures elemType;
9858
9859   // loop on nodes replacement map
9860   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9861   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9862     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9863     {
9864       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9865       nodeIDsToRemove.push_back( nToRemove->GetID() );
9866       // loop on elements sharing nToRemove
9867       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9868       while ( invElemIt->more() ) {
9869         const SMDS_MeshElement* e = invElemIt->next();
9870         // get a new suite of nodes: make replacement
9871         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9872         nodes.resize( nbNodes );
9873         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9874         while ( nIt->more() ) {
9875           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9876           nnIt = nReplaceMap.find( n );
9877           if ( nnIt != nReplaceMap.end() ) {
9878             nbReplaced++;
9879             n = (*nnIt).second;
9880           }
9881           nodes[ i++ ] = n;
9882         }
9883         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9884         //         elemIDsToRemove.push_back( e->GetID() );
9885         //       else
9886         if ( nbReplaced )
9887         {
9888           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9889           aMesh->RemoveElement( e );
9890
9891           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9892           {
9893             AddToSameGroups( newElem, e, aMesh );
9894             if ( int aShapeId = e->getshapeId() )
9895               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9896           }
9897         }
9898       }
9899     }
9900
9901   Remove( nodeIDsToRemove, true );
9902
9903   return aResult;
9904 }
9905
9906 //================================================================================
9907 /*!
9908  * \brief Find corresponding nodes in two sets of faces
9909  * \param theSide1 - first face set
9910  * \param theSide2 - second first face
9911  * \param theFirstNode1 - a boundary node of set 1
9912  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9913  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9914  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9915  * \param nReplaceMap - output map of corresponding nodes
9916  * \return bool  - is a success or not
9917  */
9918 //================================================================================
9919
9920 #ifdef _DEBUG_
9921 //#define DEBUG_MATCHING_NODES
9922 #endif
9923
9924 SMESH_MeshEditor::Sew_Error
9925 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9926                                     set<const SMDS_MeshElement*>& theSide2,
9927                                     const SMDS_MeshNode*          theFirstNode1,
9928                                     const SMDS_MeshNode*          theFirstNode2,
9929                                     const SMDS_MeshNode*          theSecondNode1,
9930                                     const SMDS_MeshNode*          theSecondNode2,
9931                                     TNodeNodeMap &                nReplaceMap)
9932 {
9933   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9934
9935   nReplaceMap.clear();
9936   //if ( theFirstNode1 != theFirstNode2 )
9937   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9938   //if ( theSecondNode1 != theSecondNode2 )
9939   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9940
9941   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9942   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9943
9944   list< NLink > linkList[2];
9945   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9946   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9947
9948   // loop on links in linkList; find faces by links and append links
9949   // of the found faces to linkList
9950   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9951   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9952     NLink link[] = { *linkIt[0], *linkIt[1] };
9953     if ( linkSet.find( link[0] ) == linkSet.end() )
9954       continue;
9955
9956     // by links, find faces in the face sets,
9957     // and find indices of link nodes in the found faces;
9958     // in a face set, there is only one or no face sharing a link
9959     // ---------------------------------------------------------------
9960
9961     const SMDS_MeshElement* face[] = { 0, 0 };
9962     list<const SMDS_MeshNode*> notLinkNodes[2];
9963     //bool reverse[] = { false, false }; // order of notLinkNodes
9964     int nbNodes[2];
9965     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9966     {
9967       const SMDS_MeshNode* n1 = link[iSide].first;
9968       const SMDS_MeshNode* n2 = link[iSide].second;
9969       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9970       set< const SMDS_MeshElement* > facesOfNode1;
9971       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9972       {
9973         // during a loop of the first node, we find all faces around n1,
9974         // during a loop of the second node, we find one face sharing both n1 and n2
9975         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9976         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9977         while ( fIt->more() ) { // loop on faces sharing a node
9978           const SMDS_MeshElement* f = fIt->next();
9979           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9980               ! facesOfNode1.insert( f ).second ) // f encounters twice
9981           {
9982             if ( face[ iSide ] ) {
9983               MESSAGE( "2 faces per link " );
9984               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9985             }
9986             face[ iSide ] = f;
9987             faceSet->erase( f );
9988
9989             // get not link nodes
9990             int nbN = f->NbNodes();
9991             if ( f->IsQuadratic() )
9992               nbN /= 2;
9993             nbNodes[ iSide ] = nbN;
9994             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9995             int i1 = f->GetNodeIndex( n1 );
9996             int i2 = f->GetNodeIndex( n2 );
9997             int iEnd = nbN, iBeg = -1, iDelta = 1;
9998             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9999             if ( reverse ) {
10000               std::swap( iEnd, iBeg ); iDelta = -1;
10001             }
10002             int i = i2;
10003             while ( true ) {
10004               i += iDelta;
10005               if ( i == iEnd ) i = iBeg + iDelta;
10006               if ( i == i1 ) break;
10007               nodes.push_back ( f->GetNode( i ) );
10008             }
10009           }
10010         }
10011       }
10012     }
10013     // check similarity of elements of the sides
10014     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10015       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10016       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10017         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10018       }
10019       else {
10020         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10021       }
10022     }
10023
10024     // set nodes to merge
10025     // -------------------
10026
10027     if ( face[0] && face[1] )  {
10028       if ( nbNodes[0] != nbNodes[1] ) {
10029         MESSAGE("Diff nb of face nodes");
10030         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10031       }
10032 #ifdef DEBUG_MATCHING_NODES
10033       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10034                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10035                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10036 #endif
10037       int nbN = nbNodes[0];
10038       {
10039         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10040         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10041         for ( int i = 0 ; i < nbN - 2; ++i ) {
10042 #ifdef DEBUG_MATCHING_NODES
10043           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10044 #endif
10045           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10046         }
10047       }
10048
10049       // add other links of the face 1 to linkList
10050       // -----------------------------------------
10051
10052       const SMDS_MeshElement* f0 = face[0];
10053       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10054       for ( int i = 0; i < nbN; i++ )
10055       {
10056         const SMDS_MeshNode* n2 = f0->GetNode( i );
10057         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10058           linkSet.insert( SMESH_TLink( n1, n2 ));
10059         if ( !iter_isnew.second ) { // already in a set: no need to process
10060           linkSet.erase( iter_isnew.first );
10061         }
10062         else // new in set == encountered for the first time: add
10063         {
10064 #ifdef DEBUG_MATCHING_NODES
10065           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10066                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10067 #endif
10068           linkList[0].push_back ( NLink( n1, n2 ));
10069           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10070         }
10071         n1 = n2;
10072       }
10073     } // 2 faces found
10074   } // loop on link lists
10075
10076   return SEW_OK;
10077 }
10078
10079 namespace // automatically find theAffectedElems for DoubleNodes()
10080 {
10081   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10082
10083   //--------------------------------------------------------------------------------
10084   // Nodes shared by adjacent FissureBorder's.
10085   // 1 node  if FissureBorder separates faces
10086   // 2 nodes if FissureBorder separates volumes
10087   struct SubBorder
10088   {
10089     const SMDS_MeshNode* _nodes[2];
10090     int                  _nbNodes;
10091
10092     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10093     {
10094       _nodes[0] = n1;
10095       _nodes[1] = n2;
10096       _nbNodes = bool( n1 ) + bool( n2 );
10097       if ( _nbNodes == 2 && n1 > n2 )
10098         std::swap( _nodes[0], _nodes[1] );
10099     }
10100     bool operator<( const SubBorder& other ) const
10101     {
10102       for ( int i = 0; i < _nbNodes; ++i )
10103       {
10104         if ( _nodes[i] < other._nodes[i] ) return true;
10105         if ( _nodes[i] > other._nodes[i] ) return false;
10106       }
10107       return false;
10108     }
10109   };
10110
10111   //--------------------------------------------------------------------------------
10112   // Map a SubBorder to all FissureBorder it bounds
10113   struct FissureBorder;
10114   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10115   typedef TBorderLinks::iterator                               TMappedSub;
10116
10117   //--------------------------------------------------------------------------------
10118   /*!
10119    * \brief Element border (volume facet or face edge) at a fissure
10120    */
10121   struct FissureBorder
10122   {
10123     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10124     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10125
10126     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10127     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10128
10129     FissureBorder( FissureBorder && from ) // move constructor
10130     {
10131       std::swap( _nodes,       from._nodes );
10132       std::swap( _sortedNodes, from._sortedNodes );
10133       _elems[0] = from._elems[0];
10134       _elems[1] = from._elems[1];
10135     }
10136
10137     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10138                    std::vector< const SMDS_MeshElement* > & adjElems)
10139       : _nodes( elemToDuplicate->NbCornerNodes() )
10140     {
10141       for ( size_t i = 0; i < _nodes.size(); ++i )
10142         _nodes[i] = elemToDuplicate->GetNode( i );
10143
10144       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10145       findAdjacent( type, adjElems );
10146     }
10147
10148     FissureBorder( const SMDS_MeshNode**                    nodes,
10149                    const size_t                             nbNodes,
10150                    const SMDSAbs_ElementType                adjElemsType,
10151                    std::vector< const SMDS_MeshElement* > & adjElems)
10152       : _nodes( nodes, nodes + nbNodes )
10153     {
10154       findAdjacent( adjElemsType, adjElems );
10155     }
10156
10157     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10158                        std::vector< const SMDS_MeshElement* > & adjElems)
10159     {
10160       _elems[0] = _elems[1] = 0;
10161       adjElems.clear();
10162       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10163         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10164           _elems[i] = adjElems[i];
10165     }
10166
10167     bool operator<( const FissureBorder& other ) const
10168     {
10169       return GetSortedNodes() < other.GetSortedNodes();
10170     }
10171
10172     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10173     {
10174       if ( _sortedNodes.empty() && !_nodes.empty() )
10175       {
10176         FissureBorder* me = const_cast<FissureBorder*>( this );
10177         me->_sortedNodes = me->_nodes;
10178         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10179       }
10180       return _sortedNodes;
10181     }
10182
10183     size_t NbSub() const
10184     {
10185       return _nodes.size();
10186     }
10187
10188     SubBorder Sub(size_t i) const
10189     {
10190       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10191     }
10192
10193     void AddSelfTo( TBorderLinks& borderLinks )
10194     {
10195       _mappedSubs.resize( NbSub() );
10196       for ( size_t i = 0; i < NbSub(); ++i )
10197       {
10198         TBorderLinks::iterator s2b =
10199           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10200         s2b->second.push_back( this );
10201         _mappedSubs[ i ] = s2b;
10202       }
10203     }
10204
10205     void Clear()
10206     {
10207       _nodes.clear();
10208     }
10209
10210     const SMDS_MeshElement* GetMarkedElem() const
10211     {
10212       if ( _nodes.empty() ) return 0; // cleared
10213       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10214       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10215       return 0;
10216     }
10217
10218     gp_XYZ GetNorm() const // normal to the border
10219     {
10220       gp_XYZ norm;
10221       if ( _nodes.size() == 2 )
10222       {
10223         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10224         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10225           avgNorm += norm;
10226         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10227           avgNorm += norm;
10228
10229         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10230         norm = bordDir ^ avgNorm;
10231       }
10232       else
10233       {
10234         SMESH_NodeXYZ p0( _nodes[0] );
10235         SMESH_NodeXYZ p1( _nodes[1] );
10236         SMESH_NodeXYZ p2( _nodes[2] );
10237         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10238       }
10239       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10240         norm.Reverse();
10241
10242       return norm;
10243     }
10244
10245     void ChooseSide() // mark an _elem located at positive side of fissure
10246     {
10247       _elems[0]->setIsMarked( true );
10248       gp_XYZ norm = GetNorm();
10249       double maxX = norm.Coord(1);
10250       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10251       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10252       if ( maxX < 0 )
10253       {
10254         _elems[0]->setIsMarked( false );
10255         if ( _elems[1] )
10256           _elems[1]->setIsMarked( true );
10257       }
10258     }
10259
10260   }; // struct FissureBorder
10261
10262   //--------------------------------------------------------------------------------
10263   /*!
10264    * \brief Classifier of elements at fissure edge
10265    */
10266   class FissureNormal
10267   {
10268     std::vector< gp_XYZ > _normals;
10269     bool                  _bothIn;
10270
10271   public:
10272     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10273     {
10274       _bothIn = false;
10275       _normals.reserve(2);
10276       _normals.push_back( bord.GetNorm() );
10277       if ( _normals.size() == 2 )
10278         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10279     }
10280
10281     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10282     {
10283       bool isIn = false;
10284       switch ( _normals.size() ) {
10285       case 1:
10286       {
10287         isIn = !isOut( n, _normals[0], elem );
10288         break;
10289       }
10290       case 2:
10291       {
10292         bool in1 = !isOut( n, _normals[0], elem );
10293         bool in2 = !isOut( n, _normals[1], elem );
10294         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10295       }
10296       }
10297       return isIn;
10298     }
10299   };
10300
10301   //================================================================================
10302   /*!
10303    * \brief Classify an element by a plane passing through a node
10304    */
10305   //================================================================================
10306
10307   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10308   {
10309     SMESH_NodeXYZ p = n;
10310     double sumDot = 0;
10311     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10312     {
10313       SMESH_NodeXYZ pi = elem->GetNode( i );
10314       sumDot += norm * ( pi - p );
10315     }
10316     return sumDot < -1e-100;
10317   }
10318
10319   //================================================================================
10320   /*!
10321    * \brief Find FissureBorder's by nodes to duplicate
10322    */
10323   //================================================================================
10324
10325   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10326                            std::vector< FissureBorder > & theFissureBorders )
10327   {
10328     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10329     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10330     if ( !n ) return;
10331     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10332     if ( n->NbInverseElements( elemType ) == 0 )
10333     {
10334       elemType = SMDSAbs_Face;
10335       if ( n->NbInverseElements( elemType ) == 0 )
10336         return;
10337     }
10338     // unmark elements touching the fissure
10339     for ( ; nIt != theNodes.end(); ++nIt )
10340       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10341
10342     // loop on elements touching the fissure to get their borders belonging to the fissure
10343     std::set< FissureBorder >              fissureBorders;
10344     std::vector< const SMDS_MeshElement* > adjElems;
10345     std::vector< const SMDS_MeshNode* >    nodes;
10346     SMDS_VolumeTool volTool;
10347     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10348     {
10349       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10350       while ( invIt->more() )
10351       {
10352         const SMDS_MeshElement* eInv = invIt->next();
10353         if ( eInv->isMarked() ) continue;
10354         eInv->setIsMarked( true );
10355
10356         if ( elemType == SMDSAbs_Volume )
10357         {
10358           volTool.Set( eInv );
10359           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10360           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10361           {
10362             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10363             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10364             nodes.clear();
10365             bool allOnFissure = true;
10366             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10367               if (( allOnFissure = theNodes.count( nn[ iN ])))
10368                 nodes.push_back( nn[ iN ]);
10369             if ( allOnFissure )
10370               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10371                                                                elemType, adjElems )));
10372           }
10373         }
10374         else // elemType == SMDSAbs_Face
10375         {
10376           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10377           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10378           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10379           {
10380             nn[1]      = eInv->GetNode( iN );
10381             onFissure1 = theNodes.count( nn[1] );
10382             if ( onFissure0 && onFissure1 )
10383               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10384             nn[0]      = nn[1];
10385             onFissure0 = onFissure1;
10386           }
10387         }
10388       }
10389     }
10390
10391     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10392     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10393     for ( ; bord != fissureBorders.end(); ++bord )
10394     {
10395       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10396     }
10397     return;
10398   } // findFissureBorders()
10399
10400   //================================================================================
10401   /*!
10402    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10403    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10404    *  \param [in] theNodesNot - nodes not to duplicate
10405    *  \param [out] theAffectedElems - the found elements
10406    */
10407   //================================================================================
10408
10409   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10410                           TIDSortedElemSet&       theAffectedElems)
10411   {
10412     if ( theElemsOrNodes.empty() ) return;
10413
10414     // find FissureBorder's
10415
10416     std::vector< FissureBorder >           fissure;
10417     std::vector< const SMDS_MeshElement* > elemsByFacet;
10418
10419     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10420     if ( (*elIt)->GetType() == SMDSAbs_Node )
10421     {
10422       findFissureBorders( theElemsOrNodes, fissure );
10423     }
10424     else
10425     {
10426       fissure.reserve( theElemsOrNodes.size() );
10427       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10428       {
10429         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10430         if ( !fissure.back()._elems[1] )
10431           fissure.pop_back();
10432       }
10433     }
10434     if ( fissure.empty() )
10435       return;
10436
10437     // fill borderLinks
10438
10439     TBorderLinks borderLinks;
10440
10441     for ( size_t i = 0; i < fissure.size(); ++i )
10442     {
10443       fissure[i].AddSelfTo( borderLinks );
10444     }
10445
10446     // get theAffectedElems
10447
10448     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10449     for ( size_t i = 0; i < fissure.size(); ++i )
10450       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10451       {
10452         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10453                                         false, /*markElem=*/true );
10454       }
10455
10456     std::vector<const SMDS_MeshNode *>                 facetNodes;
10457     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10458     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10459
10460     // choose a side of fissure
10461     fissure[0].ChooseSide();
10462     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10463
10464     size_t nbCheckedBorders = 0;
10465     while ( nbCheckedBorders < fissure.size() )
10466     {
10467       // find a FissureBorder to treat
10468       FissureBorder* bord = 0;
10469       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10470         if ( fissure[i].GetMarkedElem() )
10471           bord = & fissure[i];
10472       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10473         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10474         {
10475           bord = & fissure[i];
10476           bord->ChooseSide();
10477           theAffectedElems.insert( bord->GetMarkedElem() );
10478         }
10479       if ( !bord ) return;
10480       ++nbCheckedBorders;
10481
10482       // treat FissureBorder's linked to bord
10483       fissureNodes.clear();
10484       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10485       for ( size_t i = 0; i < bord->NbSub(); ++i )
10486       {
10487         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10488         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10489         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10490         const SubBorder&                          sb = l2b->first;
10491         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10492
10493         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10494         {
10495           for ( int j = 0; j < sb._nbNodes; ++j )
10496             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10497           continue;
10498         }
10499
10500         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10501         // until an elem adjacent to a neighbour FissureBorder is found
10502         facetNodes.clear();
10503         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10504         facetNodes.resize( sb._nbNodes + 1 );
10505
10506         while ( bordElem )
10507         {
10508           // check if bordElem is adjacent to a neighbour FissureBorder
10509           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10510           {
10511             FissureBorder* bord2 = linkedBorders[j];
10512             if ( bord2 == bord ) continue;
10513             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10514               bordElem = 0;
10515             else
10516               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10517           }
10518           if ( !bordElem )
10519             break;
10520
10521           // find the next bordElem
10522           const SMDS_MeshElement* nextBordElem = 0;
10523           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10524           {
10525             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10526             if ( fissureNodes.count( n )) continue;
10527
10528             facetNodes[ sb._nbNodes ] = n;
10529             elemsByFacet.clear();
10530             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10531             {
10532               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10533                 if ( elemsByFacet[ iE ] != bordElem &&
10534                      !elemsByFacet[ iE ]->isMarked() )
10535                 {
10536                   theAffectedElems.insert( elemsByFacet[ iE ]);
10537                   elemsByFacet[ iE ]->setIsMarked( true );
10538                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10539                     nextBordElem = elemsByFacet[ iE ];
10540                 }
10541             }
10542           }
10543           bordElem = nextBordElem;
10544
10545         } // while ( bordElem )
10546
10547         linkedBorders.clear(); // not to treat this link any more
10548
10549       } // loop on SubBorder's of a FissureBorder
10550
10551       bord->Clear();
10552
10553     } // loop on FissureBorder's
10554
10555
10556     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10557
10558     // mark nodes of theAffectedElems
10559     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10560
10561     // unmark nodes of the fissure
10562     elIt = theElemsOrNodes.begin();
10563     if ( (*elIt)->GetType() == SMDSAbs_Node )
10564       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10565     else
10566       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10567
10568     std::vector< gp_XYZ > normVec;
10569
10570     // loop on nodes of the fissure, add elements having marked nodes
10571     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10572     {
10573       const SMDS_MeshElement* e = (*elIt);
10574       if ( e->GetType() != SMDSAbs_Node )
10575         e->setIsMarked( true ); // avoid adding a fissure element
10576
10577       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10578       {
10579         const SMDS_MeshNode* n = e->GetNode( iN );
10580         if ( fissEdgeNodes2Norm.count( n ))
10581           continue;
10582
10583         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10584         while ( invIt->more() )
10585         {
10586           const SMDS_MeshElement* eInv = invIt->next();
10587           if ( eInv->isMarked() ) continue;
10588           eInv->setIsMarked( true );
10589
10590           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10591           while( nIt->more() )
10592             if ( nIt->next()->isMarked())
10593             {
10594               theAffectedElems.insert( eInv );
10595               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10596               n->setIsMarked( false );
10597               break;
10598             }
10599         }
10600       }
10601     }
10602
10603     // add elements on the fissure edge
10604     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10605     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10606     {
10607       const SMDS_MeshNode* edgeNode = n2N->first;
10608       const FissureNormal & normals = n2N->second;
10609
10610       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10611       while ( invIt->more() )
10612       {
10613         const SMDS_MeshElement* eInv = invIt->next();
10614         if ( eInv->isMarked() ) continue;
10615         eInv->setIsMarked( true );
10616
10617         // classify eInv using normals
10618         bool toAdd = normals.IsIn( edgeNode, eInv );
10619         if ( toAdd ) // check if all nodes lie on the fissure edge
10620         {
10621           bool notOnEdge = false;
10622           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10623             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10624           toAdd = notOnEdge;
10625         }
10626         if ( toAdd )
10627         {
10628           theAffectedElems.insert( eInv );
10629         }
10630       }
10631     }
10632
10633     return;
10634   } // findAffectedElems()
10635 } // namespace
10636
10637 //================================================================================
10638 /*!
10639  * \brief Create elements equal (on same nodes) to given ones
10640  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10641  *              elements of the uppest dimension are duplicated.
10642  */
10643 //================================================================================
10644
10645 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10646 {
10647   ClearLastCreated();
10648   SMESHDS_Mesh* mesh = GetMeshDS();
10649
10650   // get an element type and an iterator over elements
10651
10652   SMDSAbs_ElementType type = SMDSAbs_All;
10653   SMDS_ElemIteratorPtr elemIt;
10654   if ( theElements.empty() )
10655   {
10656     if ( mesh->NbNodes() == 0 )
10657       return;
10658     // get most complex type
10659     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10660       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10661       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10662     };
10663     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10664       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10665       {
10666         type = types[i];
10667         elemIt = mesh->elementsIterator( type );
10668         break;
10669       }
10670   }
10671   else
10672   {
10673     //type = (*theElements.begin())->GetType();
10674     elemIt = SMESHUtils::elemSetIterator( theElements );
10675   }
10676
10677   // un-mark all elements to avoid duplicating just created elements
10678   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10679
10680   // duplicate elements
10681
10682   ElemFeatures elemType;
10683
10684   vector< const SMDS_MeshNode* > nodes;
10685   while ( elemIt->more() )
10686   {
10687     const SMDS_MeshElement* elem = elemIt->next();
10688     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10689         ( elem->isMarked() ))
10690       continue;
10691
10692     elemType.Init( elem, /*basicOnly=*/false );
10693     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10694
10695     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10696       newElem->setIsMarked( true );
10697   }
10698 }
10699
10700 //================================================================================
10701 /*!
10702   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10703   \param theElems - the list of elements (edges or faces) to be replicated
10704   The nodes for duplication could be found from these elements
10705   \param theNodesNot - list of nodes to NOT replicate
10706   \param theAffectedElems - the list of elements (cells and edges) to which the
10707   replicated nodes should be associated to.
10708   \return TRUE if operation has been completed successfully, FALSE otherwise
10709 */
10710 //================================================================================
10711
10712 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10713                                     const TIDSortedElemSet& theNodesNot,
10714                                     const TIDSortedElemSet& theAffectedElems )
10715 {
10716   ClearLastCreated();
10717
10718   if ( theElems.size() == 0 )
10719     return false;
10720
10721   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10722   if ( !aMeshDS )
10723     return false;
10724
10725   bool res = false;
10726   TNodeNodeMap anOldNodeToNewNode;
10727   // duplicate elements and nodes
10728   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10729   // replce nodes by duplications
10730   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10731   return res;
10732 }
10733
10734 //================================================================================
10735 /*!
10736   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737   \param theMeshDS - mesh instance
10738   \param theElems - the elements replicated or modified (nodes should be changed)
10739   \param theNodesNot - nodes to NOT replicate
10740   \param theNodeNodeMap - relation of old node to new created node
10741   \param theIsDoubleElem - flag os to replicate element or modify
10742   \return TRUE if operation has been completed successfully, FALSE otherwise
10743 */
10744 //================================================================================
10745
10746 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10747                                    const TIDSortedElemSet& theElems,
10748                                    const TIDSortedElemSet& theNodesNot,
10749                                    TNodeNodeMap&           theNodeNodeMap,
10750                                    const bool              theIsDoubleElem )
10751 {
10752   // iterate through element and duplicate them (by nodes duplication)
10753   bool res = false;
10754   std::vector<const SMDS_MeshNode*> newNodes;
10755   ElemFeatures elemType;
10756
10757   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10758   for ( ;  elemItr != theElems.end(); ++elemItr )
10759   {
10760     const SMDS_MeshElement* anElem = *elemItr;
10761     // if (!anElem)
10762     //   continue;
10763
10764     // duplicate nodes to duplicate element
10765     bool isDuplicate = false;
10766     newNodes.resize( anElem->NbNodes() );
10767     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10768     int ind = 0;
10769     while ( anIter->more() )
10770     {
10771       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10772       const SMDS_MeshNode*  aNewNode = aCurrNode;
10773       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10774       if ( n2n != theNodeNodeMap.end() )
10775       {
10776         aNewNode = n2n->second;
10777       }
10778       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10779       {
10780         // duplicate node
10781         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10782         copyPosition( aCurrNode, aNewNode );
10783         theNodeNodeMap[ aCurrNode ] = aNewNode;
10784         myLastCreatedNodes.push_back( aNewNode );
10785       }
10786       isDuplicate |= (aCurrNode != aNewNode);
10787       newNodes[ ind++ ] = aNewNode;
10788     }
10789     if ( !isDuplicate )
10790       continue;
10791
10792     if ( theIsDoubleElem )
10793       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10794     else
10795       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10796
10797     res = true;
10798   }
10799   return res;
10800 }
10801
10802 //================================================================================
10803 /*!
10804   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10805   \param theNodes - identifiers of nodes to be doubled
10806   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10807   nodes. If list of element identifiers is empty then nodes are doubled but
10808   they not assigned to elements
10809   \return TRUE if operation has been completed successfully, FALSE otherwise
10810 */
10811 //================================================================================
10812
10813 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10814                                     const std::list< int >& theListOfModifiedElems )
10815 {
10816   ClearLastCreated();
10817
10818   if ( theListOfNodes.size() == 0 )
10819     return false;
10820
10821   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10822   if ( !aMeshDS )
10823     return false;
10824
10825   // iterate through nodes and duplicate them
10826
10827   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10828
10829   std::list< int >::const_iterator aNodeIter;
10830   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10831   {
10832     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10833     if ( !aNode )
10834       continue;
10835
10836     // duplicate node
10837
10838     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10839     if ( aNewNode )
10840     {
10841       copyPosition( aNode, aNewNode );
10842       anOldNodeToNewNode[ aNode ] = aNewNode;
10843       myLastCreatedNodes.push_back( aNewNode );
10844     }
10845   }
10846
10847   // Change nodes of elements
10848
10849   std::vector<const SMDS_MeshNode*> aNodeArr;
10850
10851   std::list< int >::const_iterator anElemIter;
10852   for ( anElemIter =  theListOfModifiedElems.begin();
10853         anElemIter != theListOfModifiedElems.end();
10854         anElemIter++ )
10855   {
10856     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10857     if ( !anElem )
10858       continue;
10859
10860     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10861     for( size_t i = 0; i < aNodeArr.size(); ++i )
10862     {
10863       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10864         anOldNodeToNewNode.find( aNodeArr[ i ]);
10865       if ( n2n != anOldNodeToNewNode.end() )
10866         aNodeArr[ i ] = n2n->second;
10867     }
10868     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10869   }
10870
10871   return true;
10872 }
10873
10874 namespace {
10875
10876   //================================================================================
10877   /*!
10878     \brief Check if element located inside shape
10879     \return TRUE if IN or ON shape, FALSE otherwise
10880   */
10881   //================================================================================
10882
10883   template<class Classifier>
10884   bool isInside(const SMDS_MeshElement* theElem,
10885                 Classifier&             theClassifier,
10886                 const double            theTol)
10887   {
10888     gp_XYZ centerXYZ (0, 0, 0);
10889     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10890       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10891
10892     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10893     theClassifier.Perform(aPnt, theTol);
10894     TopAbs_State aState = theClassifier.State();
10895     return (aState == TopAbs_IN || aState == TopAbs_ON );
10896   }
10897
10898   //================================================================================
10899   /*!
10900    * \brief Classifier of the 3D point on the TopoDS_Face
10901    *        with interaface suitable for isInside()
10902    */
10903   //================================================================================
10904
10905   struct _FaceClassifier
10906   {
10907     Extrema_ExtPS       _extremum;
10908     BRepAdaptor_Surface _surface;
10909     TopAbs_State        _state;
10910
10911     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10912     {
10913       _extremum.Initialize( _surface,
10914                             _surface.FirstUParameter(), _surface.LastUParameter(),
10915                             _surface.FirstVParameter(), _surface.LastVParameter(),
10916                             _surface.Tolerance(), _surface.Tolerance() );
10917     }
10918     void Perform(const gp_Pnt& aPnt, double theTol)
10919     {
10920       theTol *= theTol;
10921       _state = TopAbs_OUT;
10922       _extremum.Perform(aPnt);
10923       if ( _extremum.IsDone() )
10924         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10925           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10926     }
10927     TopAbs_State State() const
10928     {
10929       return _state;
10930     }
10931   };
10932 }
10933
10934 //================================================================================
10935 /*!
10936   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10937   This method is the first step of DoubleNodeElemGroupsInRegion.
10938   \param theElems - list of groups of elements (edges or faces) to be replicated
10939   \param theNodesNot - list of groups of nodes not to replicate
10940   \param theShape - shape to detect affected elements (element which geometric center
10941          located on or inside shape). If the shape is null, detection is done on faces orientations
10942          (select elements with a gravity center on the side given by faces normals).
10943          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10944          The replicated nodes should be associated to affected elements.
10945   \return true
10946   \sa DoubleNodeElemGroupsInRegion()
10947 */
10948 //================================================================================
10949
10950 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10951                                                    const TIDSortedElemSet& theNodesNot,
10952                                                    const TopoDS_Shape&     theShape,
10953                                                    TIDSortedElemSet&       theAffectedElems)
10954 {
10955   if ( theShape.IsNull() )
10956   {
10957     findAffectedElems( theElems, theAffectedElems );
10958   }
10959   else
10960   {
10961     const double aTol = Precision::Confusion();
10962     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10963     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10964     if ( theShape.ShapeType() == TopAbs_SOLID )
10965     {
10966       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10967       bsc3d->PerformInfinitePoint(aTol);
10968     }
10969     else if (theShape.ShapeType() == TopAbs_FACE )
10970     {
10971       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10972     }
10973
10974     // iterates on indicated elements and get elements by back references from their nodes
10975     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10976     for ( ;  elemItr != theElems.end(); ++elemItr )
10977     {
10978       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10979       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10980       while ( nodeItr->more() )
10981       {
10982         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10983         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10984           continue;
10985         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10986         while ( backElemItr->more() )
10987         {
10988           const SMDS_MeshElement* curElem = backElemItr->next();
10989           if ( curElem && theElems.find(curElem) == theElems.end() &&
10990                ( bsc3d.get() ?
10991                  isInside( curElem, *bsc3d, aTol ) :
10992                  isInside( curElem, *aFaceClassifier, aTol )))
10993             theAffectedElems.insert( curElem );
10994         }
10995       }
10996     }
10997   }
10998   return true;
10999 }
11000
11001 //================================================================================
11002 /*!
11003   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11004   \param theElems - group of of elements (edges or faces) to be replicated
11005   \param theNodesNot - group of nodes not to replicate
11006   \param theShape - shape to detect affected elements (element which geometric center
11007   located on or inside shape).
11008   The replicated nodes should be associated to affected elements.
11009   \return TRUE if operation has been completed successfully, FALSE otherwise
11010 */
11011 //================================================================================
11012
11013 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11014                                             const TIDSortedElemSet& theNodesNot,
11015                                             const TopoDS_Shape&     theShape )
11016 {
11017   if ( theShape.IsNull() )
11018     return false;
11019
11020   const double aTol = Precision::Confusion();
11021   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11022   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11023   if ( theShape.ShapeType() == TopAbs_SOLID )
11024   {
11025     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11026     bsc3d->PerformInfinitePoint(aTol);
11027   }
11028   else if (theShape.ShapeType() == TopAbs_FACE )
11029   {
11030     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11031   }
11032
11033   // iterates on indicated elements and get elements by back references from their nodes
11034   TIDSortedElemSet anAffected;
11035   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11036   for ( ;  elemItr != theElems.end(); ++elemItr )
11037   {
11038     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11039     if (!anElem)
11040       continue;
11041
11042     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11043     while ( nodeItr->more() )
11044     {
11045       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11046       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11047         continue;
11048       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11049       while ( backElemItr->more() )
11050       {
11051         const SMDS_MeshElement* curElem = backElemItr->next();
11052         if ( curElem && theElems.find(curElem) == theElems.end() &&
11053              ( bsc3d ?
11054                isInside( curElem, *bsc3d, aTol ) :
11055                isInside( curElem, *aFaceClassifier, aTol )))
11056           anAffected.insert( curElem );
11057       }
11058     }
11059   }
11060   return DoubleNodes( theElems, theNodesNot, anAffected );
11061 }
11062
11063 /*!
11064  *  \brief compute an oriented angle between two planes defined by four points.
11065  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11066  *  @param p0 base of the rotation axe
11067  *  @param p1 extremity of the rotation axe
11068  *  @param g1 belongs to the first plane
11069  *  @param g2 belongs to the second plane
11070  */
11071 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11072 {
11073   gp_Vec vref(p0, p1);
11074   gp_Vec v1(p0, g1);
11075   gp_Vec v2(p0, g2);
11076   gp_Vec n1 = vref.Crossed(v1);
11077   gp_Vec n2 = vref.Crossed(v2);
11078   try {
11079     return n2.AngleWithRef(n1, vref);
11080   }
11081   catch ( Standard_Failure& ) {
11082   }
11083   return Max( v1.Magnitude(), v2.Magnitude() );
11084 }
11085
11086 /*!
11087  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11088  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11089  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11090  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11091  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11092  * 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.
11093  * 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.
11094  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11095  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11096  * \param theElems - list of groups of volumes, where a group of volume is a set of
11097  *        SMDS_MeshElements sorted by Id.
11098  * \param createJointElems - if TRUE, create the elements
11099  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11100  *        the boundary between \a theDomains and the rest mesh
11101  * \return TRUE if operation has been completed successfully, FALSE otherwise
11102  */
11103 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11104                                                      bool                                 createJointElems,
11105                                                      bool                                 onAllBoundaries)
11106 {
11107   // MESSAGE("----------------------------------------------");
11108   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11109   // MESSAGE("----------------------------------------------");
11110
11111   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11112   meshDS->BuildDownWardConnectivity(true);
11113   CHRONO(50);
11114   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11115
11116   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11117   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11118   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11119
11120   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11121   std::map<int,int> celldom; // cell vtkId --> domain
11122   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11123   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11124
11125   //MESSAGE(".. Number of domains :"<<theElems.size());
11126
11127   TIDSortedElemSet theRestDomElems;
11128   const int iRestDom  = -1;
11129   const int idom0     = onAllBoundaries ? iRestDom : 0;
11130   const int nbDomains = theElems.size();
11131
11132   // Check if the domains do not share an element
11133   for (int idom = 0; idom < nbDomains-1; idom++)
11134   {
11135     //       MESSAGE("... Check of domain #" << idom);
11136     const TIDSortedElemSet& domain = theElems[idom];
11137     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11138     for (; elemItr != domain.end(); ++elemItr)
11139     {
11140       const SMDS_MeshElement* anElem = *elemItr;
11141       int idombisdeb = idom + 1 ;
11142       // check if the element belongs to a domain further in the list
11143       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11144       {
11145         const TIDSortedElemSet& domainbis = theElems[idombis];
11146         if ( domainbis.count( anElem ))
11147         {
11148           MESSAGE(".... Domain #" << idom);
11149           MESSAGE(".... Domain #" << idombis);
11150           throw SALOME_Exception("The domains are not disjoint.");
11151           return false ;
11152         }
11153       }
11154     }
11155   }
11156
11157   for (int idom = 0; idom < nbDomains; idom++)
11158   {
11159
11160     // --- build a map (face to duplicate --> volume to modify)
11161     //     with all the faces shared by 2 domains (group of elements)
11162     //     and corresponding volume of this domain, for each shared face.
11163     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11164
11165     //MESSAGE("... Neighbors of domain #" << idom);
11166     const TIDSortedElemSet& domain = theElems[idom];
11167     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11168     for (; elemItr != domain.end(); ++elemItr)
11169     {
11170       const SMDS_MeshElement* anElem = *elemItr;
11171       if (!anElem)
11172         continue;
11173       vtkIdType vtkId = anElem->GetVtkID();
11174       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11175       int neighborsVtkIds[NBMAXNEIGHBORS];
11176       int downIds[NBMAXNEIGHBORS];
11177       unsigned char downTypes[NBMAXNEIGHBORS];
11178       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11179       for (int n = 0; n < nbNeighbors; n++)
11180       {
11181         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11182         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11183         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11184         {
11185           bool ok = false;
11186           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11187           {
11188             // MESSAGE("Domain " << idombis);
11189             const TIDSortedElemSet& domainbis = theElems[idombis];
11190             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11191           }
11192           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11193           {
11194             DownIdType face(downIds[n], downTypes[n]);
11195             if (!faceDomains[face].count(idom))
11196             {
11197               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11198               celldom[vtkId] = idom;
11199               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11200             }
11201             if ( !ok )
11202             {
11203               theRestDomElems.insert( elem );
11204               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11205               celldom[neighborsVtkIds[n]] = iRestDom;
11206             }
11207           }
11208         }
11209       }
11210     }
11211   }
11212
11213   //MESSAGE("Number of shared faces " << faceDomains.size());
11214   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11215
11216   // --- explore the shared faces domain by domain,
11217   //     explore the nodes of the face and see if they belong to a cell in the domain,
11218   //     which has only a node or an edge on the border (not a shared face)
11219
11220   for (int idomain = idom0; idomain < nbDomains; idomain++)
11221   {
11222     //MESSAGE("Domain " << idomain);
11223     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11224     itface = faceDomains.begin();
11225     for (; itface != faceDomains.end(); ++itface)
11226     {
11227       const std::map<int, int>& domvol = itface->second;
11228       if (!domvol.count(idomain))
11229         continue;
11230       DownIdType face = itface->first;
11231       //MESSAGE(" --- face " << face.cellId);
11232       std::set<int> oldNodes;
11233       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11234       std::set<int>::iterator itn = oldNodes.begin();
11235       for (; itn != oldNodes.end(); ++itn)
11236       {
11237         int oldId = *itn;
11238         //MESSAGE("     node " << oldId);
11239         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11240         for (int i=0; i<l.ncells; i++)
11241         {
11242           int vtkId = l.cells[i];
11243           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11244           if (!domain.count(anElem))
11245             continue;
11246           int vtkType = grid->GetCellType(vtkId);
11247           int downId = grid->CellIdToDownId(vtkId);
11248           if (downId < 0)
11249           {
11250             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11251             continue; // not OK at this stage of the algorithm:
11252             //no cells created after BuildDownWardConnectivity
11253           }
11254           DownIdType aCell(downId, vtkType);
11255           cellDomains[aCell][idomain] = vtkId;
11256           celldom[vtkId] = idomain;
11257           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11258         }
11259       }
11260     }
11261   }
11262
11263   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11264   //     for each shared face, get the nodes
11265   //     for each node, for each domain of the face, create a clone of the node
11266
11267   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11268   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11269   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11270
11271   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11272   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11273   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11274
11275   //MESSAGE(".. Duplication of the nodes");
11276   for (int idomain = idom0; idomain < nbDomains; idomain++)
11277   {
11278     itface = faceDomains.begin();
11279     for (; itface != faceDomains.end(); ++itface)
11280     {
11281       const std::map<int, int>& domvol = itface->second;
11282       if (!domvol.count(idomain))
11283         continue;
11284       DownIdType face = itface->first;
11285       //MESSAGE(" --- face " << face.cellId);
11286       std::set<int> oldNodes;
11287       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11288       std::set<int>::iterator itn = oldNodes.begin();
11289       for (; itn != oldNodes.end(); ++itn)
11290       {
11291         int oldId = *itn;
11292         if (nodeDomains[oldId].empty())
11293         {
11294           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11295           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11296         }
11297         std::map<int, int>::const_iterator itdom = domvol.begin();
11298         for (; itdom != domvol.end(); ++itdom)
11299         {
11300           int idom = itdom->first;
11301           //MESSAGE("         domain " << idom);
11302           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11303           {
11304             if (nodeDomains[oldId].size() >= 2) // a multiple node
11305             {
11306               vector<int> orderedDoms;
11307               //MESSAGE("multiple node " << oldId);
11308               if (mutipleNodes.count(oldId))
11309                 orderedDoms = mutipleNodes[oldId];
11310               else
11311               {
11312                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11313                 for (; it != nodeDomains[oldId].end(); ++it)
11314                   orderedDoms.push_back(it->first);
11315               }
11316               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11317               //stringstream txt;
11318               //for (int i=0; i<orderedDoms.size(); i++)
11319               //  txt << orderedDoms[i] << " ";
11320               //MESSAGE("orderedDoms " << txt.str());
11321               mutipleNodes[oldId] = orderedDoms;
11322             }
11323             double *coords = grid->GetPoint(oldId);
11324             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11325             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11326             int newId = newNode->GetVtkID();
11327             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11328             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11329           }
11330         }
11331       }
11332     }
11333   }
11334
11335   //MESSAGE(".. Creation of elements");
11336   for (int idomain = idom0; idomain < nbDomains; idomain++)
11337   {
11338     itface = faceDomains.begin();
11339     for (; itface != faceDomains.end(); ++itface)
11340     {
11341       std::map<int, int> domvol = itface->second;
11342       if (!domvol.count(idomain))
11343         continue;
11344       DownIdType face = itface->first;
11345       //MESSAGE(" --- face " << face.cellId);
11346       std::set<int> oldNodes;
11347       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11348       int nbMultipleNodes = 0;
11349       std::set<int>::iterator itn = oldNodes.begin();
11350       for (; itn != oldNodes.end(); ++itn)
11351       {
11352         int oldId = *itn;
11353         if (mutipleNodes.count(oldId))
11354           nbMultipleNodes++;
11355       }
11356       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11357       {
11358         //MESSAGE("multiple Nodes detected on a shared face");
11359         int downId = itface->first.cellId;
11360         unsigned char cellType = itface->first.cellType;
11361         // --- shared edge or shared face ?
11362         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11363         {
11364           int nodes[3];
11365           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11366           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11367             if (mutipleNodes.count(nodes[i]))
11368               if (!mutipleNodesToFace.count(nodes[i]))
11369                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11370         }
11371         else // shared face (between two volumes)
11372         {
11373           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11374           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11375           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11376           for (int ie =0; ie < nbEdges; ie++)
11377           {
11378             int nodes[3];
11379             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11380             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11381             {
11382               vector<int> vn0 = mutipleNodes[nodes[0]];
11383               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11384               vector<int> doms;
11385               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11386                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11387                   if ( vn0[i0] == vn1[i1] )
11388                     doms.push_back( vn0[ i0 ]);
11389               if ( doms.size() > 2 )
11390               {
11391                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11392                 double *coords = grid->GetPoint(nodes[0]);
11393                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11394                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11395                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11396                 gp_Pnt gref;
11397                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11398                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11399                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11400                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11401                 for ( size_t id = 0; id < doms.size(); id++ )
11402                 {
11403                   int idom = doms[id];
11404                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11405                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11406                   {
11407                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11408                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11409                     if (domain.count(elem))
11410                     {
11411                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11412                       domvol[idom] = (SMDS_MeshVolume*) svol;
11413                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11414                       double values[3] = { 0,0,0 };
11415                       vtkIdType npts = 0;
11416                       vtkIdType const *pts(nullptr);
11417                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11418                       for ( vtkIdType i = 0; i < npts; ++i )
11419                       {
11420                         double *coords = grid->GetPoint( pts[i] );
11421                         for ( int j = 0; j < 3; ++j )
11422                           values[j] += coords[j] / npts;
11423                       }
11424                       if ( id == 0 )
11425                       {
11426                         gref.SetCoord( values[0], values[1], values[2] );
11427                         angleDom[idom] = 0;
11428                       }
11429                       else
11430                       {
11431                         gp_Pnt g( values[0], values[1], values[2] );
11432                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11433                         //MESSAGE("  angle=" << angleDom[idom]);
11434                       }
11435                       break;
11436                     }
11437                   }
11438                 }
11439                 map<double, int> sortedDom; // sort domains by angle
11440                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11441                   sortedDom[ia->second] = ia->first;
11442                 vector<int> vnodes;
11443                 vector<int> vdom;
11444                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11445                 {
11446                   vdom.push_back(ib->second);
11447                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11448                 }
11449                 for (int ino = 0; ino < nbNodes; ino++)
11450                   vnodes.push_back(nodes[ino]);
11451                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11452               }
11453             }
11454           }
11455         }
11456       }
11457     }
11458   }
11459
11460   // --- iterate on shared faces (volumes to modify, face to extrude)
11461   //     get node id's of the face (id SMDS = id VTK)
11462   //     create flat element with old and new nodes if requested
11463
11464   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11465   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11466
11467   std::map<int, std::map<long,int> > nodeQuadDomains;
11468   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11469
11470   //MESSAGE(".. Creation of elements: simple junction");
11471   if ( createJointElems )
11472   {
11473     string joints2DName = "joints2D";
11474     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11475     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11476     string joints3DName = "joints3D";
11477     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11478     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11479
11480     itface = faceDomains.begin();
11481     for (; itface != faceDomains.end(); ++itface)
11482     {
11483       DownIdType face = itface->first;
11484       std::set<int> oldNodes;
11485       std::set<int>::iterator itn;
11486       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11487
11488       std::map<int, int>          domvol = itface->second;
11489       std::map<int, int>::iterator itdom = domvol.begin();
11490       int     dom1 = itdom->first;
11491       int vtkVolId = itdom->second;
11492       itdom++;
11493       int           dom2 = itdom->first;
11494       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11495                                                        nodeQuadDomains);
11496       stringstream grpname;
11497       grpname << "j_";
11498       if (dom1 < dom2)
11499         grpname << dom1 << "_" << dom2;
11500       else
11501         grpname << dom2 << "_" << dom1;
11502       string namegrp = grpname.str();
11503       if (!mapOfJunctionGroups.count(namegrp))
11504         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11505       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11506       if (sgrp)
11507         sgrp->Add(vol->GetID());
11508       if (vol->GetType() == SMDSAbs_Volume)
11509         joints3DGrp->Add(vol->GetID());
11510       else if (vol->GetType() == SMDSAbs_Face)
11511         joints2DGrp->Add(vol->GetID());
11512     }
11513   }
11514
11515   // --- create volumes on multiple domain intersection if requested
11516   //     iterate on mutipleNodesToFace
11517   //     iterate on edgesMultiDomains
11518
11519   //MESSAGE(".. Creation of elements: multiple junction");
11520   if (createJointElems)
11521   {
11522     // --- iterate on mutipleNodesToFace
11523
11524     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11525     for (; itn != mutipleNodesToFace.end(); ++itn)
11526     {
11527       int node = itn->first;
11528       vector<int> orderDom = itn->second;
11529       vector<vtkIdType> orderedNodes;
11530       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11531         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11532       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11533
11534       stringstream grpname;
11535       grpname << "m2j_";
11536       grpname << 0 << "_" << 0;
11537       string namegrp = grpname.str();
11538       if (!mapOfJunctionGroups.count(namegrp))
11539         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11540       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11541       if (sgrp)
11542         sgrp->Add(face->GetID());
11543     }
11544
11545     // --- iterate on edgesMultiDomains
11546
11547     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11548     for (; ite != edgesMultiDomains.end(); ++ite)
11549     {
11550       vector<int>    nodes = ite->first;
11551       vector<int> orderDom = ite->second;
11552       vector<vtkIdType> orderedNodes;
11553       if (nodes.size() == 2)
11554       {
11555         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11556         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11557           if ( orderDom.size() == 3 )
11558             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11559               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11560           else
11561             for (int idom = orderDom.size()-1; idom >=0; idom--)
11562               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11563         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11564
11565         string namegrp = "jointsMultiples";
11566         if (!mapOfJunctionGroups.count(namegrp))
11567           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11568         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11569         if (sgrp)
11570           sgrp->Add(vol->GetID());
11571       }
11572       else
11573       {
11574         //INFOS("Quadratic multiple joints not implemented");
11575         // TODO quadratic nodes
11576       }
11577     }
11578   }
11579
11580   // --- list the explicit faces and edges of the mesh that need to be modified,
11581   //     i.e. faces and edges built with one or more duplicated nodes.
11582   //     associate these faces or edges to their corresponding domain.
11583   //     only the first domain found is kept when a face or edge is shared
11584
11585   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11586   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11587
11588   //MESSAGE(".. Modification of elements");
11589   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11590   for (int idomain = idom0; idomain < nbDomains; idomain++)
11591   {
11592     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11593     for (; itnod != nodeDomains.end(); ++itnod)
11594     {
11595       int oldId = itnod->first;
11596       //MESSAGE("     node " << oldId);
11597       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11598       for (int i = 0; i < l.ncells; i++)
11599       {
11600         int vtkId = l.cells[i];
11601         int vtkType = grid->GetCellType(vtkId);
11602         int downId = grid->CellIdToDownId(vtkId);
11603         if (downId < 0)
11604           continue; // new cells: not to be modified
11605         DownIdType aCell(downId, vtkType);
11606         int volParents[1000];
11607         int nbvol = 0;
11608         nbvol = grid->GetParentVolumes(volParents, vtkId);
11609         if ( domainType == SMDSAbs_Volume )
11610         {
11611           nbvol = grid->GetParentVolumes(volParents, vtkId);
11612         }
11613         else // domainType == SMDSAbs_Face
11614         {
11615           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11616           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11617           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11618           for (int i=0; i< nbFaces; i++)
11619           {
11620             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11621             if (vtkFaceId >= 0)
11622               volParents[nbvol++] = vtkFaceId;
11623           }
11624         }
11625         for (int j = 0; j < nbvol; j++)
11626           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11627             if (!feDom.count(vtkId))
11628             {
11629               feDom[vtkId] = idomain;
11630               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11631               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11632               //        << " type " << vtkType << " downId " << downId);
11633             }
11634       }
11635     }
11636   }
11637
11638   // --- iterate on shared faces (volumes to modify, face to extrude)
11639   //     get node id's of the face
11640   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11641
11642   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11643   for (int m=0; m<3; m++)
11644   {
11645     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11646     itface = (*amap).begin();
11647     for (; itface != (*amap).end(); ++itface)
11648     {
11649       DownIdType face = itface->first;
11650       std::set<int> oldNodes;
11651       std::set<int>::iterator itn;
11652       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11653       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11654       std::map<int, int> localClonedNodeIds;
11655
11656       std::map<int, int> domvol = itface->second;
11657       std::map<int, int>::iterator itdom = domvol.begin();
11658       for (; itdom != domvol.end(); ++itdom)
11659       {
11660         int idom = itdom->first;
11661         int vtkVolId = itdom->second;
11662         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11663         localClonedNodeIds.clear();
11664         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11665         {
11666           int oldId = *itn;
11667           if (nodeDomains[oldId].count(idom))
11668           {
11669             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11670             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11671           }
11672         }
11673         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11674       }
11675     }
11676   }
11677
11678   // Remove empty groups (issue 0022812)
11679   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11680   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11681   {
11682     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11683       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11684   }
11685
11686   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11687   grid->DeleteLinks();
11688
11689   CHRONOSTOP(50);
11690   counters::stats();
11691   return true;
11692 }
11693
11694 /*!
11695  * \brief Double nodes on some external faces and create flat elements.
11696  * Flat elements are mainly used by some types of mechanic calculations.
11697  *
11698  * Each group of the list must be constituted of faces.
11699  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11700  * @param theElems - list of groups of faces, where a group of faces is a set of
11701  * SMDS_MeshElements sorted by Id.
11702  * @return TRUE if operation has been completed successfully, FALSE otherwise
11703  */
11704 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11705 {
11706   // MESSAGE("-------------------------------------------------");
11707   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11708   // MESSAGE("-------------------------------------------------");
11709
11710   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11711
11712   // --- For each group of faces
11713   //     duplicate the nodes, create a flat element based on the face
11714   //     replace the nodes of the faces by their clones
11715
11716   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11717   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11718   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11719
11720   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11721   {
11722     const TIDSortedElemSet&           domain = theElems[idom];
11723     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11724     for ( ; elemItr != domain.end(); ++elemItr )
11725     {
11726       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11727       if (!aFace)
11728         continue;
11729       // MESSAGE("aFace=" << aFace->GetID());
11730       bool isQuad = aFace->IsQuadratic();
11731       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11732
11733       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11734
11735       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11736       while (nodeIt->more())
11737       {
11738         const SMDS_MeshNode* node = nodeIt->next();
11739         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11740         if (isMedium)
11741           ln2.push_back(node);
11742         else
11743           ln0.push_back(node);
11744
11745         const SMDS_MeshNode* clone = 0;
11746         if (!clonedNodes.count(node))
11747         {
11748           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11749           copyPosition( node, clone );
11750           clonedNodes[node] = clone;
11751         }
11752         else
11753           clone = clonedNodes[node];
11754
11755         if (isMedium)
11756           ln3.push_back(clone);
11757         else
11758           ln1.push_back(clone);
11759
11760         const SMDS_MeshNode* inter = 0;
11761         if (isQuad && (!isMedium))
11762         {
11763           if (!intermediateNodes.count(node))
11764           {
11765             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11766             copyPosition( node, inter );
11767             intermediateNodes[node] = inter;
11768           }
11769           else
11770             inter = intermediateNodes[node];
11771           ln4.push_back(inter);
11772         }
11773       }
11774
11775       // --- extrude the face
11776
11777       vector<const SMDS_MeshNode*> ln;
11778       SMDS_MeshVolume* vol = 0;
11779       vtkIdType aType = aFace->GetVtkType();
11780       switch (aType)
11781       {
11782       case VTK_TRIANGLE:
11783         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11784         // MESSAGE("vol prism " << vol->GetID());
11785         ln.push_back(ln1[0]);
11786         ln.push_back(ln1[1]);
11787         ln.push_back(ln1[2]);
11788         break;
11789       case VTK_QUAD:
11790         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11791         // MESSAGE("vol hexa " << vol->GetID());
11792         ln.push_back(ln1[0]);
11793         ln.push_back(ln1[1]);
11794         ln.push_back(ln1[2]);
11795         ln.push_back(ln1[3]);
11796         break;
11797       case VTK_QUADRATIC_TRIANGLE:
11798         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11799                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11800         // MESSAGE("vol quad prism " << vol->GetID());
11801         ln.push_back(ln1[0]);
11802         ln.push_back(ln1[1]);
11803         ln.push_back(ln1[2]);
11804         ln.push_back(ln3[0]);
11805         ln.push_back(ln3[1]);
11806         ln.push_back(ln3[2]);
11807         break;
11808       case VTK_QUADRATIC_QUAD:
11809         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11810         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11811         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11812         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11813                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11814                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11815         // MESSAGE("vol quad hexa " << vol->GetID());
11816         ln.push_back(ln1[0]);
11817         ln.push_back(ln1[1]);
11818         ln.push_back(ln1[2]);
11819         ln.push_back(ln1[3]);
11820         ln.push_back(ln3[0]);
11821         ln.push_back(ln3[1]);
11822         ln.push_back(ln3[2]);
11823         ln.push_back(ln3[3]);
11824         break;
11825       case VTK_POLYGON:
11826         break;
11827       default:
11828         break;
11829       }
11830
11831       if (vol)
11832       {
11833         stringstream grpname;
11834         grpname << "jf_";
11835         grpname << idom;
11836         string namegrp = grpname.str();
11837         if (!mapOfJunctionGroups.count(namegrp))
11838           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11839         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11840         if (sgrp)
11841           sgrp->Add(vol->GetID());
11842       }
11843
11844       // --- modify the face
11845
11846       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11847     }
11848   }
11849   return true;
11850 }
11851
11852 /*!
11853  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11854  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11855  *  groups of faces to remove inside the object, (idem edges).
11856  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11857  */
11858 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11859                                       const TopoDS_Shape&             theShape,
11860                                       SMESH_NodeSearcher*             theNodeSearcher,
11861                                       const char*                     groupName,
11862                                       std::vector<double>&            nodesCoords,
11863                                       std::vector<std::vector<int> >& listOfListOfNodes)
11864 {
11865   // MESSAGE("--------------------------------");
11866   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11867   // MESSAGE("--------------------------------");
11868
11869   // --- zone of volumes to remove is given :
11870   //     1 either by a geom shape (one or more vertices) and a radius,
11871   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11872   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11873   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11874   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11875   //     defined by it's name.
11876
11877   SMESHDS_GroupBase* groupDS = 0;
11878   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11879   while ( groupIt->more() )
11880   {
11881     groupDS = 0;
11882     SMESH_Group * group = groupIt->next();
11883     if ( !group ) continue;
11884     groupDS = group->GetGroupDS();
11885     if ( !groupDS || groupDS->IsEmpty() ) continue;
11886     std::string grpName = group->GetName();
11887     //MESSAGE("grpName=" << grpName);
11888     if (grpName == groupName)
11889       break;
11890     else
11891       groupDS = 0;
11892   }
11893
11894   bool isNodeGroup = false;
11895   bool isNodeCoords = false;
11896   if (groupDS)
11897   {
11898     if (groupDS->GetType() != SMDSAbs_Node)
11899       return;
11900     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11901   }
11902
11903   if (nodesCoords.size() > 0)
11904     isNodeCoords = true; // a list o nodes given by their coordinates
11905   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11906
11907   // --- define groups to build
11908
11909   // --- group of SMDS volumes
11910   string grpvName = groupName;
11911   grpvName += "_vol";
11912   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11913   if (!grp)
11914   {
11915     MESSAGE("group not created " << grpvName);
11916     return;
11917   }
11918   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11919
11920   // --- group of SMDS faces on the skin
11921   string grpsName = groupName;
11922   grpsName += "_skin";
11923   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11924   if (!grps)
11925   {
11926     MESSAGE("group not created " << grpsName);
11927     return;
11928   }
11929   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11930
11931   // --- group of SMDS faces internal (several shapes)
11932   string grpiName = groupName;
11933   grpiName += "_internalFaces";
11934   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11935   if (!grpi)
11936   {
11937     MESSAGE("group not created " << grpiName);
11938     return;
11939   }
11940   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11941
11942   // --- group of SMDS faces internal (several shapes)
11943   string grpeiName = groupName;
11944   grpeiName += "_internalEdges";
11945   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11946   if (!grpei)
11947   {
11948     MESSAGE("group not created " << grpeiName);
11949     return;
11950   }
11951   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11952
11953   // --- build downward connectivity
11954
11955   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11956   meshDS->BuildDownWardConnectivity(true);
11957   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11958
11959   // --- set of volumes detected inside
11960
11961   std::set<int> setOfInsideVol;
11962   std::set<int> setOfVolToCheck;
11963
11964   std::vector<gp_Pnt> gpnts;
11965
11966   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11967   {
11968     //MESSAGE("group of nodes provided");
11969     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11970     while ( elemIt->more() )
11971     {
11972       const SMDS_MeshElement* elem = elemIt->next();
11973       if (!elem)
11974         continue;
11975       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11976       if (!node)
11977         continue;
11978       SMDS_MeshElement* vol = 0;
11979       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11980       while (volItr->more())
11981       {
11982         vol = (SMDS_MeshElement*)volItr->next();
11983         setOfInsideVol.insert(vol->GetVtkID());
11984         sgrp->Add(vol->GetID());
11985       }
11986     }
11987   }
11988   else if (isNodeCoords)
11989   {
11990     //MESSAGE("list of nodes coordinates provided");
11991     size_t i = 0;
11992     int k = 0;
11993     while ( i < nodesCoords.size()-2 )
11994     {
11995       double x = nodesCoords[i++];
11996       double y = nodesCoords[i++];
11997       double z = nodesCoords[i++];
11998       gp_Pnt p = gp_Pnt(x, y ,z);
11999       gpnts.push_back(p);
12000       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12001       k++;
12002     }
12003   }
12004   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12005   {
12006     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12007     TopTools_IndexedMapOfShape vertexMap;
12008     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12009     gp_Pnt p = gp_Pnt(0,0,0);
12010     if (vertexMap.Extent() < 1)
12011       return;
12012
12013     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12014     {
12015       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12016       p = BRep_Tool::Pnt(vertex);
12017       gpnts.push_back(p);
12018       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12019     }
12020   }
12021
12022   if (gpnts.size() > 0)
12023   {
12024     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12025     //MESSAGE("startNode->nodeId " << nodeId);
12026
12027     double radius2 = radius*radius;
12028     //MESSAGE("radius2 " << radius2);
12029
12030     // --- volumes on start node
12031
12032     setOfVolToCheck.clear();
12033     SMDS_MeshElement* startVol = 0;
12034     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12035     while (volItr->more())
12036     {
12037       startVol = (SMDS_MeshElement*)volItr->next();
12038       setOfVolToCheck.insert(startVol->GetVtkID());
12039     }
12040     if (setOfVolToCheck.empty())
12041     {
12042       MESSAGE("No volumes found");
12043       return;
12044     }
12045
12046     // --- starting with central volumes then their neighbors, check if they are inside
12047     //     or outside the domain, until no more new neighbor volume is inside.
12048     //     Fill the group of inside volumes
12049
12050     std::map<int, double> mapOfNodeDistance2;
12051     std::set<int> setOfOutsideVol;
12052     while (!setOfVolToCheck.empty())
12053     {
12054       std::set<int>::iterator it = setOfVolToCheck.begin();
12055       int vtkId = *it;
12056       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12057       bool volInside = false;
12058       vtkIdType npts = 0;
12059       vtkIdType const *pts(nullptr);
12060       grid->GetCellPoints(vtkId, npts, pts);
12061       for (int i=0; i<npts; i++)
12062       {
12063         double distance2 = 0;
12064         if (mapOfNodeDistance2.count(pts[i]))
12065         {
12066           distance2 = mapOfNodeDistance2[pts[i]];
12067           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12068         }
12069         else
12070         {
12071           double *coords = grid->GetPoint(pts[i]);
12072           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12073           distance2 = 1.E40;
12074           for ( size_t j = 0; j < gpnts.size(); j++ )
12075           {
12076             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12077             if (d2 < distance2)
12078             {
12079               distance2 = d2;
12080               if (distance2 < radius2)
12081                 break;
12082             }
12083           }
12084           mapOfNodeDistance2[pts[i]] = distance2;
12085           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12086         }
12087         if (distance2 < radius2)
12088         {
12089           volInside = true; // one or more nodes inside the domain
12090           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12091           break;
12092         }
12093       }
12094       if (volInside)
12095       {
12096         setOfInsideVol.insert(vtkId);
12097         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12098         int neighborsVtkIds[NBMAXNEIGHBORS];
12099         int downIds[NBMAXNEIGHBORS];
12100         unsigned char downTypes[NBMAXNEIGHBORS];
12101         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12102         for (int n = 0; n < nbNeighbors; n++)
12103           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12104             setOfVolToCheck.insert(neighborsVtkIds[n]);
12105       }
12106       else
12107       {
12108         setOfOutsideVol.insert(vtkId);
12109         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12110       }
12111       setOfVolToCheck.erase(vtkId);
12112     }
12113   }
12114
12115   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12116   //     If yes, add the volume to the inside set
12117
12118   bool addedInside = true;
12119   std::set<int> setOfVolToReCheck;
12120   while (addedInside)
12121   {
12122     //MESSAGE(" --------------------------- re check");
12123     addedInside = false;
12124     std::set<int>::iterator itv = setOfInsideVol.begin();
12125     for (; itv != setOfInsideVol.end(); ++itv)
12126     {
12127       int vtkId = *itv;
12128       int neighborsVtkIds[NBMAXNEIGHBORS];
12129       int downIds[NBMAXNEIGHBORS];
12130       unsigned char downTypes[NBMAXNEIGHBORS];
12131       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12132       for (int n = 0; n < nbNeighbors; n++)
12133         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12134           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12135     }
12136     setOfVolToCheck = setOfVolToReCheck;
12137     setOfVolToReCheck.clear();
12138     while  (!setOfVolToCheck.empty())
12139     {
12140       std::set<int>::iterator it = setOfVolToCheck.begin();
12141       int vtkId = *it;
12142       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12143       {
12144         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12145         int countInside = 0;
12146         int neighborsVtkIds[NBMAXNEIGHBORS];
12147         int downIds[NBMAXNEIGHBORS];
12148         unsigned char downTypes[NBMAXNEIGHBORS];
12149         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12150         for (int n = 0; n < nbNeighbors; n++)
12151           if (setOfInsideVol.count(neighborsVtkIds[n]))
12152             countInside++;
12153         //MESSAGE("countInside " << countInside);
12154         if (countInside > 1)
12155         {
12156           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12157           setOfInsideVol.insert(vtkId);
12158           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12159           addedInside = true;
12160         }
12161         else
12162           setOfVolToReCheck.insert(vtkId);
12163       }
12164       setOfVolToCheck.erase(vtkId);
12165     }
12166   }
12167
12168   // --- map of Downward faces at the boundary, inside the global volume
12169   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12170   //     fill group of SMDS faces inside the volume (when several volume shapes)
12171   //     fill group of SMDS faces on the skin of the global volume (if skin)
12172
12173   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12174   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12175   std::set<int>::iterator it = setOfInsideVol.begin();
12176   for (; it != setOfInsideVol.end(); ++it)
12177   {
12178     int vtkId = *it;
12179     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12180     int neighborsVtkIds[NBMAXNEIGHBORS];
12181     int downIds[NBMAXNEIGHBORS];
12182     unsigned char downTypes[NBMAXNEIGHBORS];
12183     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12184     for (int n = 0; n < nbNeighbors; n++)
12185     {
12186       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12187       if (neighborDim == 3)
12188       {
12189         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12190         {
12191           DownIdType face(downIds[n], downTypes[n]);
12192           boundaryFaces[face] = vtkId;
12193         }
12194         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12195         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12196         if (vtkFaceId >= 0)
12197         {
12198           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12199           // find also the smds edges on this face
12200           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12201           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12202           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12203           for (int i = 0; i < nbEdges; i++)
12204           {
12205             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12206             if (vtkEdgeId >= 0)
12207               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12208           }
12209         }
12210       }
12211       else if (neighborDim == 2) // skin of the volume
12212       {
12213         DownIdType face(downIds[n], downTypes[n]);
12214         skinFaces[face] = vtkId;
12215         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12216         if (vtkFaceId >= 0)
12217           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12218       }
12219     }
12220   }
12221
12222   // --- identify the edges constituting the wire of each subshape on the skin
12223   //     define polylines with the nodes of edges, equivalent to wires
12224   //     project polylines on subshapes, and partition, to get geom faces
12225
12226   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12227   std::set<int>                 shapeIds;
12228
12229   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12230   while (itelem->more())
12231   {
12232     const SMDS_MeshElement *elem = itelem->next();
12233     int shapeId = elem->getshapeId();
12234     int   vtkId = elem->GetVtkID();
12235     if (!shapeIdToVtkIdSet.count(shapeId))
12236     {
12237       shapeIds.insert(shapeId);
12238     }
12239     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12240   }
12241
12242   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12243   std::set<DownIdType, DownIdCompare> emptyEdges;
12244
12245   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12246   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12247   {
12248     int shapeId = itShape->first;
12249     //MESSAGE(" --- Shape ID --- "<< shapeId);
12250     shapeIdToEdges[shapeId] = emptyEdges;
12251
12252     std::vector<int> nodesEdges;
12253
12254     std::set<int>::iterator its = itShape->second.begin();
12255     for (; its != itShape->second.end(); ++its)
12256     {
12257       int vtkId = *its;
12258       //MESSAGE("     " << vtkId);
12259       int neighborsVtkIds[NBMAXNEIGHBORS];
12260       int downIds[NBMAXNEIGHBORS];
12261       unsigned char downTypes[NBMAXNEIGHBORS];
12262       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12263       for (int n = 0; n < nbNeighbors; n++)
12264       {
12265         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12266           continue;
12267         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12268         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12269         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12270         {
12271           DownIdType edge(downIds[n], downTypes[n]);
12272           if (!shapeIdToEdges[shapeId].count(edge))
12273           {
12274             shapeIdToEdges[shapeId].insert(edge);
12275             int vtkNodeId[3];
12276             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12277             nodesEdges.push_back(vtkNodeId[0]);
12278             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12279             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12280           }
12281         }
12282       }
12283     }
12284
12285     std::list<int> order;
12286     if (nodesEdges.size() > 0)
12287     {
12288       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12289       nodesEdges[0] = -1;
12290       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12291       nodesEdges[1] = -1; // do not reuse this edge
12292       bool found = true;
12293       while (found)
12294       {
12295         int nodeTofind = order.back(); // try first to push back
12296         int i = 0;
12297         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12298           if (nodesEdges[i] == nodeTofind)
12299             break;
12300         if ( i == (int) nodesEdges.size() )
12301           found = false; // no follower found on back
12302         else
12303         {
12304           if (i%2) // odd ==> use the previous one
12305             if (nodesEdges[i-1] < 0)
12306               found = false;
12307             else
12308             {
12309               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12310               nodesEdges[i-1] = -1;
12311             }
12312           else // even ==> use the next one
12313             if (nodesEdges[i+1] < 0)
12314               found = false;
12315             else
12316             {
12317               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12318               nodesEdges[i+1] = -1;
12319             }
12320         }
12321         if (found)
12322           continue;
12323         // try to push front
12324         found = true;
12325         nodeTofind = order.front(); // try to push front
12326         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12327           if ( nodesEdges[i] == nodeTofind )
12328             break;
12329         if ( i == (int)nodesEdges.size() )
12330         {
12331           found = false; // no predecessor found on front
12332           continue;
12333         }
12334         if (i%2) // odd ==> use the previous one
12335           if (nodesEdges[i-1] < 0)
12336             found = false;
12337           else
12338           {
12339             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12340             nodesEdges[i-1] = -1;
12341           }
12342         else // even ==> use the next one
12343           if (nodesEdges[i+1] < 0)
12344             found = false;
12345           else
12346           {
12347             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12348             nodesEdges[i+1] = -1;
12349           }
12350       }
12351     }
12352
12353
12354     std::vector<int> nodes;
12355     nodes.push_back(shapeId);
12356     std::list<int>::iterator itl = order.begin();
12357     for (; itl != order.end(); itl++)
12358     {
12359       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12360       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12361     }
12362     listOfListOfNodes.push_back(nodes);
12363   }
12364
12365   //     partition geom faces with blocFissure
12366   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12367   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12368
12369   return;
12370 }
12371
12372
12373 //================================================================================
12374 /*!
12375  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12376  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12377  * \return TRUE if operation has been completed successfully, FALSE otherwise
12378  */
12379 //================================================================================
12380
12381 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12382 {
12383   // iterates on volume elements and detect all free faces on them
12384   SMESHDS_Mesh* aMesh = GetMeshDS();
12385   if (!aMesh)
12386     return false;
12387
12388   ElemFeatures faceType( SMDSAbs_Face );
12389   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12390   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12391   while(vIt->more())
12392   {
12393     const SMDS_MeshVolume* volume = vIt->next();
12394     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12395     vTool.SetExternalNormal();
12396     const int iQuad = volume->IsQuadratic();
12397     faceType.SetQuad( iQuad );
12398     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12399     {
12400       if (!vTool.IsFreeFace(iface))
12401         continue;
12402       nbFree++;
12403       vector<const SMDS_MeshNode *> nodes;
12404       int nbFaceNodes = vTool.NbFaceNodes(iface);
12405       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12406       int inode = 0;
12407       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12408         nodes.push_back(faceNodes[inode]);
12409
12410       if (iQuad) // add medium nodes
12411       {
12412         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12413           nodes.push_back(faceNodes[inode]);
12414         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12415           nodes.push_back(faceNodes[8]);
12416       }
12417       // add new face based on volume nodes
12418       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12419       {
12420         nbExisted++; // face already exists
12421       }
12422       else
12423       {
12424         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12425         nbCreated++;
12426       }
12427     }
12428   }
12429   return ( nbFree == ( nbExisted + nbCreated ));
12430 }
12431
12432 namespace
12433 {
12434   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12435   {
12436     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12437       return n;
12438     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12439   }
12440 }
12441 //================================================================================
12442 /*!
12443  * \brief Creates missing boundary elements
12444  *  \param elements - elements whose boundary is to be checked
12445  *  \param dimension - defines type of boundary elements to create
12446  *  \param group - a group to store created boundary elements in
12447  *  \param targetMesh - a mesh to store created boundary elements in
12448  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12449  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12450  *                                boundary elements will be copied into the targetMesh
12451  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12452  *                                boundary elements will be added into the new group
12453  *  \param aroundElements - if true, elements will be created on boundary of given
12454  *                          elements else, on boundary of the whole mesh.
12455  * \return nb of added boundary elements
12456  */
12457 //================================================================================
12458
12459 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12460                                        Bnd_Dimension           dimension,
12461                                        SMESH_Group*            group/*=0*/,
12462                                        SMESH_Mesh*             targetMesh/*=0*/,
12463                                        bool                    toCopyElements/*=false*/,
12464                                        bool                    toCopyExistingBoundary/*=false*/,
12465                                        bool                    toAddExistingBondary/*= false*/,
12466                                        bool                    aroundElements/*= false*/)
12467 {
12468   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12469   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12470   // hope that all elements are of the same type, do not check them all
12471   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12472     throw SALOME_Exception(LOCALIZED("wrong element type"));
12473
12474   if ( !targetMesh )
12475     toCopyElements = toCopyExistingBoundary = false;
12476
12477   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12478   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12479   int nbAddedBnd = 0;
12480
12481   // editor adding present bnd elements and optionally holding elements to add to the group
12482   SMESH_MeshEditor* presentEditor;
12483   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12484   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12485
12486   SMESH_MesherHelper helper( *myMesh );
12487   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12488   SMDS_VolumeTool vTool;
12489   TIDSortedElemSet avoidSet;
12490   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12491   size_t inode;
12492
12493   typedef vector<const SMDS_MeshNode*> TConnectivity;
12494   TConnectivity tgtNodes;
12495   ElemFeatures elemKind( missType ), elemToCopy;
12496
12497   vector<const SMDS_MeshElement*> presentBndElems;
12498   vector<TConnectivity>           missingBndElems;
12499   vector<int>                     freeFacets;
12500   TConnectivity nodes, elemNodes;
12501
12502   SMDS_ElemIteratorPtr eIt;
12503   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12504   else                  eIt = SMESHUtils::elemSetIterator( elements );
12505
12506   while ( eIt->more() )
12507   {
12508     const SMDS_MeshElement* elem = eIt->next();
12509     const int              iQuad = elem->IsQuadratic();
12510     elemKind.SetQuad( iQuad );
12511
12512     // ------------------------------------------------------------------------------------
12513     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12514     // ------------------------------------------------------------------------------------
12515     presentBndElems.clear();
12516     missingBndElems.clear();
12517     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12518     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12519     {
12520       const SMDS_MeshElement* otherVol = 0;
12521       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12522       {
12523         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12524              ( !aroundElements || elements.count( otherVol )))
12525           continue;
12526         freeFacets.push_back( iface );
12527       }
12528       if ( missType == SMDSAbs_Face )
12529         vTool.SetExternalNormal();
12530       for ( size_t i = 0; i < freeFacets.size(); ++i )
12531       {
12532         int                iface = freeFacets[i];
12533         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12534         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12535         if ( missType == SMDSAbs_Edge ) // boundary edges
12536         {
12537           nodes.resize( 2+iQuad );
12538           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12539           {
12540             for ( size_t j = 0; j < nodes.size(); ++j )
12541               nodes[ j ] = nn[ i+j ];
12542             if ( const SMDS_MeshElement* edge =
12543                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12544               presentBndElems.push_back( edge );
12545             else
12546               missingBndElems.push_back( nodes );
12547           }
12548         }
12549         else // boundary face
12550         {
12551           nodes.clear();
12552           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12553             nodes.push_back( nn[inode] ); // add corner nodes
12554           if (iQuad)
12555             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12556               nodes.push_back( nn[inode] ); // add medium nodes
12557           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12558           if ( iCenter > 0 )
12559             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12560
12561           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12562                                                                SMDSAbs_Face, /*noMedium=*/false ))
12563             presentBndElems.push_back( f );
12564           else
12565             missingBndElems.push_back( nodes );
12566
12567           if ( targetMesh != myMesh )
12568           {
12569             // add 1D elements on face boundary to be added to a new mesh
12570             const SMDS_MeshElement* edge;
12571             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12572             {
12573               if ( iQuad )
12574                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12575               else
12576                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12577               if ( edge && avoidSet.insert( edge ).second )
12578                 presentBndElems.push_back( edge );
12579             }
12580           }
12581         }
12582       }
12583     }
12584     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12585     {
12586       avoidSet.clear(), avoidSet.insert( elem );
12587       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12588                         SMDS_MeshElement::iterator() );
12589       elemNodes.push_back( elemNodes[0] );
12590       nodes.resize( 2 + iQuad );
12591       const int nbLinks = elem->NbCornerNodes();
12592       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12593       {
12594         nodes[0] = elemNodes[iN];
12595         nodes[1] = elemNodes[iN+1+iQuad];
12596         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12597           continue; // not free link
12598
12599         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12600         if ( const SMDS_MeshElement* edge =
12601              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12602           presentBndElems.push_back( edge );
12603         else
12604           missingBndElems.push_back( nodes );
12605       }
12606     }
12607
12608     // ---------------------------------
12609     // 2. Add missing boundary elements
12610     // ---------------------------------
12611     if ( targetMesh != myMesh )
12612       // instead of making a map of nodes in this mesh and targetMesh,
12613       // we create nodes with same IDs.
12614       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12615       {
12616         TConnectivity& srcNodes = missingBndElems[i];
12617         tgtNodes.resize( srcNodes.size() );
12618         for ( inode = 0; inode < srcNodes.size(); ++inode )
12619           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12620         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12621                                                                        missType,
12622                                                                        /*noMedium=*/false))
12623           continue;
12624         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12625         ++nbAddedBnd;
12626       }
12627     else
12628       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12629       {
12630         TConnectivity& nodes = missingBndElems[ i ];
12631         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12632                                                                        missType,
12633                                                                        /*noMedium=*/false))
12634           continue;
12635         SMDS_MeshElement* newElem =
12636           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12637         nbAddedBnd += bool( newElem );
12638
12639         // try to set a new element to a shape
12640         if ( myMesh->HasShapeToMesh() )
12641         {
12642           bool ok = true;
12643           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12644           const size_t nbN = nodes.size() / (iQuad+1 );
12645           for ( inode = 0; inode < nbN && ok; ++inode )
12646           {
12647             pair<int, TopAbs_ShapeEnum> i_stype =
12648               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12649             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12650               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12651           }
12652           if ( ok && mediumShapes.size() > 1 )
12653           {
12654             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12655             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12656             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12657             {
12658               if (( ok = ( stype_i->first != stype_i_0.first )))
12659                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12660                                         aMesh->IndexToShape( stype_i_0.second ));
12661             }
12662           }
12663           if ( ok && mediumShapes.begin()->first == missShapeType )
12664             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12665         }
12666       }
12667
12668     // ----------------------------------
12669     // 3. Copy present boundary elements
12670     // ----------------------------------
12671     if ( toCopyExistingBoundary )
12672       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12673       {
12674         const SMDS_MeshElement* e = presentBndElems[i];
12675         tgtNodes.resize( e->NbNodes() );
12676         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12677           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12678         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12679       }
12680     else // store present elements to add them to a group
12681       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12682       {
12683         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12684       }
12685
12686   } // loop on given elements
12687
12688   // ---------------------------------------------
12689   // 4. Fill group with boundary elements
12690   // ---------------------------------------------
12691   if ( group )
12692   {
12693     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12694       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12695         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12696   }
12697   tgtEditor.myLastCreatedElems.clear();
12698   tgtEditor2.myLastCreatedElems.clear();
12699
12700   // -----------------------
12701   // 5. Copy given elements
12702   // -----------------------
12703   if ( toCopyElements && targetMesh != myMesh )
12704   {
12705     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12706     else                  eIt = SMESHUtils::elemSetIterator( elements );
12707     while (eIt->more())
12708     {
12709       const SMDS_MeshElement* elem = eIt->next();
12710       tgtNodes.resize( elem->NbNodes() );
12711       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12712         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12713       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12714
12715       tgtEditor.myLastCreatedElems.clear();
12716     }
12717   }
12718   return nbAddedBnd;
12719 }
12720
12721 //================================================================================
12722 /*!
12723  * \brief Copy node position and set \a to node on the same geometry
12724  */
12725 //================================================================================
12726
12727 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12728                                      const SMDS_MeshNode* to )
12729 {
12730   if ( !from || !to ) return;
12731
12732   SMDS_PositionPtr pos = from->GetPosition();
12733   if ( !pos || from->getshapeId() < 1 ) return;
12734
12735   switch ( pos->GetTypeOfPosition() )
12736   {
12737   case SMDS_TOP_3DSPACE: break;
12738
12739   case SMDS_TOP_FACE:
12740   {
12741     SMDS_FacePositionPtr fPos = pos;
12742     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12743                                 fPos->GetUParameter(), fPos->GetVParameter() );
12744     break;
12745   }
12746   case SMDS_TOP_EDGE:
12747   {
12748     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12749     SMDS_EdgePositionPtr ePos = pos;
12750     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12751     break;
12752   }
12753   case SMDS_TOP_VERTEX:
12754   {
12755     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12756     break;
12757   }
12758   case SMDS_TOP_UNSPEC:
12759   default:;
12760   }
12761 }