Salome HOME
bos #26453: SMESH: uniform refinement
[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 theRefFaces.
1165  *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1166  * \param theRefFaces - correctly oriented faces whose orientation defines
1167  *        orientation of other faces.
1168  * \return number of reoriented faces.
1169  */
1170 //================================================================================
1171
1172 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
1173                                   const gp_Vec&       theDirection,
1174                                   TIDSortedElemSet &  theRefFaces,
1175                                   bool                theAllowNonManifold )
1176 {
1177   int nbReori = 0;
1178
1179   if ( theFaces.empty() )
1180   {
1181     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1182     while ( fIt->more() )
1183       theFaces.insert( theFaces.end(), fIt->next() );
1184
1185     if ( theFaces.empty() )
1186       return nbReori;
1187   }
1188
1189   // orient theRefFaces according to theDirection
1190   if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1191     for ( const SMDS_MeshElement* refFace : theRefFaces )
1192     {
1193       gp_XYZ normal;
1194       SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1195       if ( normal * theDirection.XYZ() < 0 )
1196         nbReori += Reorient( refFace );
1197     }
1198
1199   // mark reference faces
1200   GetMeshDS()->SetAllCellsNotMarked();
1201   for ( const SMDS_MeshElement* refFace : theRefFaces )
1202     refFace->setIsMarked( true );
1203
1204   // erase reference faces from theFaces
1205   for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1206     if ( (*fIt)->isMarked() )
1207       fIt = theFaces.erase( fIt );
1208     else
1209       ++fIt;
1210
1211   if ( theRefFaces.empty() )
1212   {
1213     theRefFaces.insert( *theFaces.begin() );
1214     theFaces.erase( theFaces.begin() );
1215   }
1216
1217   // Orient theFaces
1218
1219   // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1220   //   theFaces.erase( theFace );
1221
1222   int nodeInd1, nodeInd2;
1223   const SMDS_MeshElement*           refFace, *otherFace;
1224   vector< const SMDS_MeshElement* > facesNearLink;
1225   vector< std::pair< int, int > >   nodeIndsOfFace;
1226   TIDSortedElemSet                  avoidSet, emptySet;
1227   NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1228
1229   while ( !theRefFaces.empty() )
1230   {
1231     auto refFaceIt = theRefFaces.begin();
1232     refFace = *refFaceIt;
1233     theRefFaces.erase( refFaceIt );
1234
1235     avoidSet.clear();
1236     avoidSet.insert( refFace );
1237
1238     NLink link( refFace->GetNode( 0 ), nullptr );
1239
1240     const int nbNodes = refFace->NbCornerNodes();
1241     for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1242     {
1243       link.second = refFace->GetNode(( i+1 ) % nbNodes );
1244       bool isLinkVisited = checkedLinks.Contains( link );
1245       if ( isLinkVisited )
1246       {
1247         // link has already been checked and won't be encountered more
1248         // if the group (theFaces) is manifold
1249         //checkedLinks.erase( linkIt_isNew.first );
1250       }
1251       else
1252       {
1253         checkedLinks.Add( link );
1254
1255         facesNearLink.clear();
1256         nodeIndsOfFace.clear();
1257         TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1258
1259         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1260                                                              emptySet, avoidSet,
1261                                                              &nodeInd1, &nodeInd2 )))
1262         {
1263           if (( otherFace->isMarked() ) || // ref face
1264               (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1265           {
1266             facesNearLink.push_back( otherFace );
1267             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1268           }
1269           avoidSet.insert( otherFace );
1270         }
1271         if ( facesNearLink.size() > 1 )
1272         {
1273           // NON-MANIFOLD mesh shell !
1274           if ( !theAllowNonManifold )
1275           {
1276             throw SALOME_Exception("Non-manifold topology of groups");
1277           }
1278           // select a face most co-directed with refFace,
1279           // other faces won't be visited this time
1280           gp_XYZ NF, NOF;
1281           SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1282           double proj, maxProj = -1;
1283           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1284           {
1285             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1286             if (( proj = Abs( NF * NOF )) > maxProj )
1287             {
1288               maxProj = proj;
1289               otherFace = facesNearLink[i];
1290               nodeInd1  = nodeIndsOfFace[i].first;
1291               nodeInd2  = nodeIndsOfFace[i].second;
1292             }
1293           }
1294           // not to visit rejected faces
1295           // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1296           //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1297           //     visitedFaces.insert( facesNearLink[i] );
1298         }
1299         else if ( facesNearLink.size() == 1 )
1300         {
1301           otherFace = facesNearLink[0];
1302           nodeInd1  = nodeIndsOfFace.back().first;
1303           nodeInd2  = nodeIndsOfFace.back().second;
1304         }
1305         if ( otherFace )
1306         {
1307           // link must be reverse in otherFace if orientation of otherFace
1308           // is same as that of refFace
1309           if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1310           {
1311             if ( otherFace->isMarked() )
1312               throw SALOME_Exception("Different orientation of reference faces");
1313             nbReori += Reorient( otherFace );
1314           }
1315           if ( !otherFace->isMarked() )
1316           {
1317             theRefFaces.insert( otherFace );
1318             if ( objFaceIt != theFaces.end() )
1319               theFaces.erase( objFaceIt );
1320           }
1321         }
1322       }
1323       link.first = link.second; // reverse the link
1324
1325     } // loop on links of refFace
1326
1327     if ( theRefFaces.empty() && !theFaces.empty() )
1328     {
1329       theRefFaces.insert( *theFaces.begin() );
1330       theFaces.erase( theFaces.begin() );
1331     }
1332
1333   } // while ( !theRefFaces.empty() )
1334
1335   return nbReori;
1336 }
1337
1338 //================================================================================
1339 /*!
1340  * \brief Reorient faces basing on orientation of adjacent volumes.
1341  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1342  * \param theVolumes - reference volumes.
1343  * \param theOutsideNormal - to orient faces to have their normal
1344  *        pointing either \a outside or \a inside the adjacent volumes.
1345  * \return number of reoriented faces.
1346  */
1347 //================================================================================
1348
1349 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1350                                       TIDSortedElemSet & theVolumes,
1351                                       const bool         theOutsideNormal)
1352 {
1353   int nbReori = 0;
1354
1355   SMDS_ElemIteratorPtr faceIt;
1356   if ( theFaces.empty() )
1357     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1358   else
1359     faceIt = SMESHUtils::elemSetIterator( theFaces );
1360
1361   vector< const SMDS_MeshNode* > faceNodes;
1362   TIDSortedElemSet checkedVolumes;
1363   set< const SMDS_MeshNode* > faceNodesSet;
1364   SMDS_VolumeTool volumeTool;
1365
1366   while ( faceIt->more() ) // loop on given faces
1367   {
1368     const SMDS_MeshElement* face = faceIt->next();
1369     if ( face->GetType() != SMDSAbs_Face )
1370       continue;
1371
1372     const size_t nbCornersNodes = face->NbCornerNodes();
1373     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1374
1375     checkedVolumes.clear();
1376     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1377     while ( vIt->more() )
1378     {
1379       const SMDS_MeshElement* volume = vIt->next();
1380
1381       if ( !checkedVolumes.insert( volume ).second )
1382         continue;
1383       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1384         continue;
1385
1386       // is volume adjacent?
1387       bool allNodesCommon = true;
1388       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1389         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1390       if ( !allNodesCommon )
1391         continue;
1392
1393       // get nodes of a corresponding volume facet
1394       faceNodesSet.clear();
1395       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1396       volumeTool.Set( volume );
1397       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1398       if ( facetID < 0 ) continue;
1399       volumeTool.SetExternalNormal();
1400       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1401
1402       // compare order of faceNodes and facetNodes
1403       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1404       int iNN[2];
1405       for ( int i = 0; i < 2; ++i )
1406       {
1407         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1408         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1409           if ( faceNodes[ iN ] == n )
1410           {
1411             iNN[ i ] = iN;
1412             break;
1413           }
1414       }
1415       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1416       if ( isOutside != theOutsideNormal )
1417         nbReori += Reorient( face );
1418     }
1419   }  // loop on given faces
1420
1421   return nbReori;
1422 }
1423
1424 //=======================================================================
1425 //function : getBadRate
1426 //purpose  :
1427 //=======================================================================
1428
1429 static double getBadRate (const SMDS_MeshElement*               theElem,
1430                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1431 {
1432   SMESH::Controls::TSequenceOfXYZ P;
1433   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1434     return 1e100;
1435   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1436   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1437 }
1438
1439 //=======================================================================
1440 //function : QuadToTri
1441 //purpose  : Cut quadrangles into triangles.
1442 //           theCrit is used to select a diagonal to cut
1443 //=======================================================================
1444
1445 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1446                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1447 {
1448   ClearLastCreated();
1449
1450   if ( !theCrit.get() )
1451     return false;
1452
1453   SMESHDS_Mesh *       aMesh = GetMeshDS();
1454   Handle(Geom_Surface) surface;
1455   SMESH_MesherHelper   helper( *GetMesh() );
1456
1457   myLastCreatedElems.reserve( theElems.size() * 2 );
1458
1459   TIDSortedElemSet::iterator itElem;
1460   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1461   {
1462     const SMDS_MeshElement* elem = *itElem;
1463     if ( !elem || elem->GetType() != SMDSAbs_Face )
1464       continue;
1465     if ( elem->NbCornerNodes() != 4 )
1466       continue;
1467
1468     // retrieve element nodes
1469     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1470
1471     // compare two sets of possible triangles
1472     double aBadRate1, aBadRate2; // to what extent a set is bad
1473     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1474     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1475     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1476
1477     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1478     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1479     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1480
1481     const int aShapeId = FindShape( elem );
1482     const SMDS_MeshElement* newElem1 = 0;
1483     const SMDS_MeshElement* newElem2 = 0;
1484
1485     if ( !elem->IsQuadratic() ) // split linear quadrangle
1486     {
1487       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1488       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1489       if ( aBadRate1 <= aBadRate2 ) {
1490         // tr1 + tr2 is better
1491         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1492         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1493       }
1494       else {
1495         // tr3 + tr4 is better
1496         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1497         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1498       }
1499     }
1500     else // split quadratic quadrangle
1501     {
1502       helper.SetIsQuadratic( true );
1503       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1504
1505       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1506       if ( aNodes.size() == 9 )
1507       {
1508         helper.SetIsBiQuadratic( true );
1509         if ( aBadRate1 <= aBadRate2 )
1510           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1511         else
1512           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1513       }
1514       // create a new element
1515       if ( aBadRate1 <= aBadRate2 ) {
1516         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1517         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1518       }
1519       else {
1520         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1521         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1522       }
1523     } // quadratic case
1524
1525     // care of a new element
1526
1527     myLastCreatedElems.push_back(newElem1);
1528     myLastCreatedElems.push_back(newElem2);
1529     AddToSameGroups( newElem1, elem, aMesh );
1530     AddToSameGroups( newElem2, elem, aMesh );
1531
1532     // put a new triangle on the same shape
1533     if ( aShapeId )
1534       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1535     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1536
1537     aMesh->RemoveElement( elem );
1538   }
1539   return true;
1540 }
1541
1542 //=======================================================================
1543 /*!
1544  * \brief Split each of given quadrangles into 4 triangles.
1545  * \param theElems - The faces to be split. If empty all faces are split.
1546  */
1547 //=======================================================================
1548
1549 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1550 {
1551   ClearLastCreated();
1552   myLastCreatedElems.reserve( theElems.size() * 4 );
1553
1554   SMESH_MesherHelper helper( *GetMesh() );
1555   helper.SetElementsOnShape( true );
1556
1557   // get standalone groups of faces
1558   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1559   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1560     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1561       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1562         allFaceGroups.push_back( & group->SMDSGroup() );
1563
1564   bool   checkUV;
1565   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1566   gp_XYZ xyz[9];
1567   vector< const SMDS_MeshNode* > nodes;
1568   SMESHDS_SubMesh*               subMeshDS = 0;
1569   TopoDS_Face                    F;
1570   Handle(Geom_Surface)           surface;
1571   TopLoc_Location                loc;
1572
1573   SMDS_ElemIteratorPtr faceIt;
1574   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1575   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1576
1577   while ( faceIt->more() )
1578   {
1579     const SMDS_MeshElement* quad = faceIt->next();
1580     if ( !quad || quad->NbCornerNodes() != 4 )
1581       continue;
1582
1583     // get a surface the quad is on
1584
1585     if ( quad->getshapeId() < 1 )
1586     {
1587       F.Nullify();
1588       helper.SetSubShape( 0 );
1589       subMeshDS = 0;
1590     }
1591     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1592     {
1593       helper.SetSubShape( quad->getshapeId() );
1594       if ( !helper.GetSubShape().IsNull() &&
1595            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1596       {
1597         F = TopoDS::Face( helper.GetSubShape() );
1598         surface = BRep_Tool::Surface( F, loc );
1599         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1600       }
1601       else
1602       {
1603         helper.SetSubShape( 0 );
1604         subMeshDS = 0;
1605       }
1606     }
1607
1608     // create a central node
1609
1610     const SMDS_MeshNode* nCentral;
1611     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1612
1613     if ( nodes.size() == 9 )
1614     {
1615       nCentral = nodes.back();
1616     }
1617     else
1618     {
1619       size_t iN = 0;
1620       if ( F.IsNull() )
1621       {
1622         for ( ; iN < nodes.size(); ++iN )
1623           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1624
1625         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1626           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1627
1628         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1629                                    xyz[0], xyz[1], xyz[2], xyz[3],
1630                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1631       }
1632       else
1633       {
1634         for ( ; iN < nodes.size(); ++iN )
1635           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1636
1637         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1638           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1639
1640         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1641                                   uv[0], uv[1], uv[2], uv[3],
1642                                   uv[4], uv[5], uv[6], uv[7] );
1643
1644         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1645         xyz[ 8 ] = p.XYZ();
1646       }
1647
1648       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1649                                  uv[8].X(), uv[8].Y() );
1650       myLastCreatedNodes.push_back( nCentral );
1651     }
1652
1653     helper.SetIsQuadratic  ( nodes.size() > 4 );
1654     helper.SetIsBiQuadratic( nodes.size() == 9 );
1655     if ( helper.GetIsQuadratic() )
1656       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1657
1658     // select groups to update
1659     faceGroups.clear();
1660     for ( SMDS_MeshGroup* group : allFaceGroups )
1661       if ( group->Remove( quad ))
1662         faceGroups.push_back( group );
1663
1664     // create 4 triangles
1665
1666     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1667
1668     for ( int i = 0; i < 4; ++i )
1669     {
1670       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1671                                                nodes[(i+1)%4],
1672                                                nCentral );
1673       myLastCreatedElems.push_back( tria );
1674       for ( SMDS_MeshGroup* group : faceGroups )
1675         group->Add( tria );
1676     }
1677   }
1678 }
1679
1680 //=======================================================================
1681 //function : BestSplit
1682 //purpose  : Find better diagonal for cutting.
1683 //=======================================================================
1684
1685 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1686                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1687 {
1688   ClearLastCreated();
1689
1690   if (!theCrit.get())
1691     return -1;
1692
1693   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1694     return -1;
1695
1696   if( theQuad->NbNodes()==4 ||
1697       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1698
1699     // retrieve element nodes
1700     const SMDS_MeshNode* aNodes [4];
1701     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1702     int i = 0;
1703     //while (itN->more())
1704     while (i<4) {
1705       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1706     }
1707     // compare two sets of possible triangles
1708     double aBadRate1, aBadRate2; // to what extent a set is bad
1709     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1710     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1711     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1712
1713     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1714     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1715     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1716     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1717     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1718     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1719       return 1; // diagonal 1-3
1720
1721     return 2; // diagonal 2-4
1722   }
1723   return -1;
1724 }
1725
1726 namespace
1727 {
1728   // Methods of splitting volumes into tetra
1729
1730   const int theHexTo5_1[5*4+1] =
1731     {
1732       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1733     };
1734   const int theHexTo5_2[5*4+1] =
1735     {
1736       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1737     };
1738   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1739
1740   const int theHexTo6_1[6*4+1] =
1741     {
1742       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
1743     };
1744   const int theHexTo6_2[6*4+1] =
1745     {
1746       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
1747     };
1748   const int theHexTo6_3[6*4+1] =
1749     {
1750       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
1751     };
1752   const int theHexTo6_4[6*4+1] =
1753     {
1754       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
1755     };
1756   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1757
1758   const int thePyraTo2_1[2*4+1] =
1759     {
1760       0, 1, 2, 4,    0, 2, 3, 4,   -1
1761     };
1762   const int thePyraTo2_2[2*4+1] =
1763     {
1764       1, 2, 3, 4,    1, 3, 0, 4,   -1
1765     };
1766   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1767
1768   const int thePentaTo3_1[3*4+1] =
1769     {
1770       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1771     };
1772   const int thePentaTo3_2[3*4+1] =
1773     {
1774       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1775     };
1776   const int thePentaTo3_3[3*4+1] =
1777     {
1778       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1779     };
1780   const int thePentaTo3_4[3*4+1] =
1781     {
1782       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1783     };
1784   const int thePentaTo3_5[3*4+1] =
1785     {
1786       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1787     };
1788   const int thePentaTo3_6[3*4+1] =
1789     {
1790       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1791     };
1792   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1793                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1794
1795   // Methods of splitting hexahedron into prisms
1796
1797   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1798     {
1799       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
1800     };
1801   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1802     {
1803       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
1804     };
1805   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1806     {
1807       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
1808     };
1809
1810   const int theHexTo2Prisms_BT_1[6*2+1] =
1811     {
1812       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1813     };
1814   const int theHexTo2Prisms_BT_2[6*2+1] =
1815     {
1816       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1817     };
1818   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1819
1820   const int theHexTo2Prisms_LR_1[6*2+1] =
1821     {
1822       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1823     };
1824   const int theHexTo2Prisms_LR_2[6*2+1] =
1825     {
1826       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1827     };
1828   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1829
1830   const int theHexTo2Prisms_FB_1[6*2+1] =
1831     {
1832       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1833     };
1834   const int theHexTo2Prisms_FB_2[6*2+1] =
1835     {
1836       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1837     };
1838   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1839
1840
1841   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1842   {
1843     int _n1, _n2, _n3;
1844     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1845     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1846     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1847                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1848   };
1849   struct TSplitMethod
1850   {
1851     int        _nbSplits;
1852     int        _nbCorners;
1853     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1854     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1855     bool       _ownConn;      //!< to delete _connectivity in destructor
1856     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1857
1858     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1859       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1860     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1861     TSplitMethod(const TSplitMethod &splitMethod)
1862       : _nbSplits(splitMethod._nbSplits),
1863         _nbCorners(splitMethod._nbCorners),
1864         _baryNode(splitMethod._baryNode),
1865         _ownConn(splitMethod._ownConn),
1866         _faceBaryNode(splitMethod._faceBaryNode)
1867     {
1868       _connectivity = splitMethod._connectivity;
1869       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1870       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1871     }
1872     bool hasFacet( const TTriangleFacet& facet ) const
1873     {
1874       if ( _nbCorners == 4 )
1875       {
1876         const int* tetConn = _connectivity;
1877         for ( ; tetConn[0] >= 0; tetConn += 4 )
1878           if (( facet.contains( tetConn[0] ) +
1879                 facet.contains( tetConn[1] ) +
1880                 facet.contains( tetConn[2] ) +
1881                 facet.contains( tetConn[3] )) == 3 )
1882             return true;
1883       }
1884       else // prism, _nbCorners == 6
1885       {
1886         const int* prismConn = _connectivity;
1887         for ( ; prismConn[0] >= 0; prismConn += 6 )
1888         {
1889           if (( facet.contains( prismConn[0] ) &&
1890                 facet.contains( prismConn[1] ) &&
1891                 facet.contains( prismConn[2] ))
1892               ||
1893               ( facet.contains( prismConn[3] ) &&
1894                 facet.contains( prismConn[4] ) &&
1895                 facet.contains( prismConn[5] )))
1896             return true;
1897         }
1898       }
1899       return false;
1900     }
1901   };
1902
1903   //=======================================================================
1904   /*!
1905    * \brief return TSplitMethod for the given element to split into tetrahedra
1906    */
1907   //=======================================================================
1908
1909   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1910   {
1911     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1912
1913     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1914     // an edge and a face barycenter; tertaherdons are based on triangles and
1915     // a volume barycenter
1916     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1917
1918     // Find out how adjacent volumes are split
1919
1920     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1921     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1922     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1923     {
1924       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1925       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1926       if ( nbNodes < 4 ) continue;
1927
1928       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1929       const int* nInd = vol.GetFaceNodesIndices( iF );
1930       if ( nbNodes == 4 )
1931       {
1932         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1933         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1934         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1935         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1936       }
1937       else
1938       {
1939         int iCom = 0; // common node of triangle faces to split into
1940         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1941         {
1942           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1943                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1944                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1945           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1946                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1947                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1948           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1949           {
1950             triaSplits.push_back( t012 );
1951             triaSplits.push_back( t023 );
1952             break;
1953           }
1954         }
1955       }
1956       if ( !triaSplits.empty() )
1957         hasAdjacentSplits = true;
1958     }
1959
1960     // Among variants of split method select one compliant with adjacent volumes
1961
1962     TSplitMethod method;
1963     if ( !vol.Element()->IsPoly() && !is24TetMode )
1964     {
1965       int nbVariants = 2, nbTet = 0;
1966       const int** connVariants = 0;
1967       switch ( vol.Element()->GetEntityType() )
1968       {
1969       case SMDSEntity_Hexa:
1970       case SMDSEntity_Quad_Hexa:
1971       case SMDSEntity_TriQuad_Hexa:
1972         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1973           connVariants = theHexTo5, nbTet = 5;
1974         else
1975           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1976         break;
1977       case SMDSEntity_Pyramid:
1978       case SMDSEntity_Quad_Pyramid:
1979         connVariants = thePyraTo2;  nbTet = 2;
1980         break;
1981       case SMDSEntity_Penta:
1982       case SMDSEntity_Quad_Penta:
1983       case SMDSEntity_BiQuad_Penta:
1984         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1985         break;
1986       default:
1987         nbVariants = 0;
1988       }
1989       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1990       {
1991         // check method compliance with adjacent tetras,
1992         // all found splits must be among facets of tetras described by this method
1993         method = TSplitMethod( nbTet, connVariants[variant] );
1994         if ( hasAdjacentSplits && method._nbSplits > 0 )
1995         {
1996           bool facetCreated = true;
1997           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1998           {
1999             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2000             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2001               facetCreated = method.hasFacet( *facet );
2002           }
2003           if ( !facetCreated )
2004             method = TSplitMethod(0); // incompatible method
2005         }
2006       }
2007     }
2008     if ( method._nbSplits < 1 )
2009     {
2010       // No standard method is applicable, use a generic solution:
2011       // each facet of a volume is split into triangles and
2012       // each of triangles and a volume barycenter form a tetrahedron.
2013
2014       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2015
2016       int* connectivity = new int[ maxTetConnSize + 1 ];
2017       method._connectivity = connectivity;
2018       method._ownConn = true;
2019       method._baryNode = !isHex27; // to create central node or not
2020
2021       int connSize = 0;
2022       int baryCenInd = vol.NbNodes() - int( isHex27 );
2023       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2024       {
2025         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2026         const int*   nInd = vol.GetFaceNodesIndices( iF );
2027         // find common node of triangle facets of tetra to create
2028         int iCommon = 0; // index in linear numeration
2029         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2030         if ( !triaSplits.empty() )
2031         {
2032           // by found facets
2033           const TTriangleFacet* facet = &triaSplits.front();
2034           for ( ; iCommon < nbNodes-1 ; ++iCommon )
2035             if ( facet->contains( nInd[ iQ * iCommon ]) &&
2036                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2037               break;
2038         }
2039         else if ( nbNodes > 3 && !is24TetMode )
2040         {
2041           // find the best method of splitting into triangles by aspect ratio
2042           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2043           map< double, int > badness2iCommon;
2044           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2045           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2046           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2047           {
2048             double badness = 0;
2049             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2050             {
2051               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2052                                       nodes[ iQ*((iLast-1)%nbNodes)],
2053                                       nodes[ iQ*((iLast  )%nbNodes)]);
2054               badness += getBadRate( &tria, aspectRatio );
2055             }
2056             badness2iCommon.insert( make_pair( badness, iCommon ));
2057           }
2058           // use iCommon with lowest badness
2059           iCommon = badness2iCommon.begin()->second;
2060         }
2061         if ( iCommon >= nbNodes )
2062           iCommon = 0; // something wrong
2063
2064         // fill connectivity of tetrahedra based on a current face
2065         int nbTet = nbNodes - 2;
2066         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2067         {
2068           int faceBaryCenInd;
2069           if ( isHex27 )
2070           {
2071             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2072             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2073           }
2074           else
2075           {
2076             method._faceBaryNode[ iF ] = 0;
2077             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2078           }
2079           nbTet = nbNodes;
2080           for ( int i = 0; i < nbTet; ++i )
2081           {
2082             int i1 = i, i2 = (i+1) % nbNodes;
2083             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2084             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2085             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2086             connectivity[ connSize++ ] = faceBaryCenInd;
2087             connectivity[ connSize++ ] = baryCenInd;
2088           }
2089         }
2090         else
2091         {
2092           for ( int i = 0; i < nbTet; ++i )
2093           {
2094             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2095             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2096             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2097             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2098             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2099             connectivity[ connSize++ ] = baryCenInd;
2100           }
2101         }
2102         method._nbSplits += nbTet;
2103
2104       } // loop on volume faces
2105
2106       connectivity[ connSize++ ] = -1;
2107
2108     } // end of generic solution
2109
2110     return method;
2111   }
2112   //=======================================================================
2113   /*!
2114    * \brief return TSplitMethod to split haxhedron into prisms
2115    */
2116   //=======================================================================
2117
2118   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2119                                     const int        methodFlags,
2120                                     const int        facetToSplit)
2121   {
2122     TSplitMethod method;
2123
2124     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2125     // B, T, L, B, R, F
2126     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2127
2128     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2129     {
2130       static TSplitMethod to4methods[4]; // order BT, LR, FB
2131       if ( to4methods[iF]._nbSplits == 0 )
2132       {
2133         switch ( iF ) {
2134         case 0:
2135           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2136           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2137           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2138           break;
2139         case 1:
2140           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2141           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2142           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2143           break;
2144         case 2:
2145           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2146           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2147           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2148           break;
2149         default: return to4methods[3];
2150         }
2151         to4methods[iF]._nbSplits  = 4;
2152         to4methods[iF]._nbCorners = 6;
2153       }
2154       method = to4methods[iF];
2155       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2156       return method;
2157     }
2158     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2159
2160     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2161
2162     const int nbVariants = 2, nbSplits = 2;
2163     const int** connVariants = 0;
2164     switch ( iF ) {
2165     case 0: connVariants = theHexTo2Prisms_BT; break;
2166     case 1: connVariants = theHexTo2Prisms_LR; break;
2167     case 2: connVariants = theHexTo2Prisms_FB; break;
2168     default: return method;
2169     }
2170
2171     // look for prisms adjacent via facetToSplit and an opposite one
2172     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2173     {
2174       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2175       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2176       if ( nbNodes != 4 ) return method;
2177
2178       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2179       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2180       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2181       TTriangleFacet* t;
2182       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2183         t = &t012;
2184       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2185         t = &t123;
2186       else
2187         continue;
2188
2189       // there are adjacent prism
2190       for ( int variant = 0; variant < nbVariants; ++variant )
2191       {
2192         // check method compliance with adjacent prisms,
2193         // the found prism facets must be among facets of prisms described by current method
2194         method._nbSplits     = nbSplits;
2195         method._nbCorners    = 6;
2196         method._connectivity = connVariants[ variant ];
2197         if ( method.hasFacet( *t ))
2198           return method;
2199       }
2200     }
2201
2202     // No adjacent prisms. Select a variant with a best aspect ratio.
2203
2204     double badness[2] = { 0., 0. };
2205     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2206     const SMDS_MeshNode** nodes = vol.GetNodes();
2207     for ( int variant = 0; variant < nbVariants; ++variant )
2208       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2209       {
2210         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2211         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2212
2213         method._connectivity = connVariants[ variant ];
2214         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2215         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2216         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2217
2218         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2219                                 nodes[ t->_n2 ],
2220                                 nodes[ t->_n3 ] );
2221         badness[ variant ] += getBadRate( &tria, aspectRatio );
2222       }
2223     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2224
2225     method._nbSplits     = nbSplits;
2226     method._nbCorners    = 6;
2227     method._connectivity = connVariants[ iBetter ];
2228
2229     return method;
2230   }
2231
2232   //================================================================================
2233   /*!
2234    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2235    */
2236   //================================================================================
2237
2238   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2239                                        const SMDSAbs_GeometryType geom ) const
2240   {
2241     // find the tetrahedron including the three nodes of facet
2242     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2243     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2244     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2245     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2246     while ( volIt1->more() )
2247     {
2248       const SMDS_MeshElement* v = volIt1->next();
2249       if ( v->GetGeomType() != geom )
2250         continue;
2251       const int lastCornerInd = v->NbCornerNodes() - 1;
2252       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2253         continue; // medium node not allowed
2254       const int ind2 = v->GetNodeIndex( n2 );
2255       if ( ind2 < 0 || lastCornerInd < ind2 )
2256         continue;
2257       const int ind3 = v->GetNodeIndex( n3 );
2258       if ( ind3 < 0 || lastCornerInd < ind3 )
2259         continue;
2260       return true;
2261     }
2262     return false;
2263   }
2264
2265   //=======================================================================
2266   /*!
2267    * \brief A key of a face of volume
2268    */
2269   //=======================================================================
2270
2271   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2272   {
2273     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2274     {
2275       TIDSortedNodeSet sortedNodes;
2276       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2277       int nbNodes = vol.NbFaceNodes( iF );
2278       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2279       for ( int i = 0; i < nbNodes; i += iQ )
2280         sortedNodes.insert( fNodes[i] );
2281       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2282       first.first   = (*(n++))->GetID();
2283       first.second  = (*(n++))->GetID();
2284       second.first  = (*(n++))->GetID();
2285       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2286     }
2287   };
2288 } // namespace
2289
2290 //=======================================================================
2291 //function : SplitVolumes
2292 //purpose  : Split volume elements into tetrahedra or prisms.
2293 //           If facet ID < 0, element is split into tetrahedra,
2294 //           else a hexahedron is split into prisms so that the given facet is
2295 //           split into triangles
2296 //=======================================================================
2297
2298 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2299                                      const int            theMethodFlags)
2300 {
2301   SMDS_VolumeTool    volTool;
2302   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2303   fHelper.ToFixNodeParameters( true );
2304
2305   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2306   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2307
2308   SMESH_SequenceOfElemPtr newNodes, newElems;
2309
2310   // map face of volume to it's baricenrtic node
2311   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2312   double bc[3];
2313   vector<const SMDS_MeshElement* > splitVols;
2314
2315   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2316   for ( ; elem2facet != theElems.end(); ++elem2facet )
2317   {
2318     const SMDS_MeshElement* elem = elem2facet->first;
2319     const int       facetToSplit = elem2facet->second;
2320     if ( elem->GetType() != SMDSAbs_Volume )
2321       continue;
2322     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2323     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2324       continue;
2325
2326     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2327
2328     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2329                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2330                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2331     if ( splitMethod._nbSplits < 1 ) continue;
2332
2333     // find submesh to add new tetras to
2334     if ( !subMesh || !subMesh->Contains( elem ))
2335     {
2336       int shapeID = FindShape( elem );
2337       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2338       subMesh = GetMeshDS()->MeshElements( shapeID );
2339     }
2340     int iQ;
2341     if ( elem->IsQuadratic() )
2342     {
2343       iQ = 2;
2344       // add quadratic links to the helper
2345       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2346       {
2347         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2348         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2349         for ( int iN = 0; iN < nbN; iN += iQ )
2350           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2351       }
2352       helper.SetIsQuadratic( true );
2353     }
2354     else
2355     {
2356       iQ = 1;
2357       helper.SetIsQuadratic( false );
2358     }
2359     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2360                                         volTool.GetNodes() + elem->NbNodes() );
2361     helper.SetElementsOnShape( true );
2362     if ( splitMethod._baryNode )
2363     {
2364       // make a node at barycenter
2365       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2366       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2367       nodes.push_back( gcNode );
2368       newNodes.push_back( gcNode );
2369     }
2370     if ( !splitMethod._faceBaryNode.empty() )
2371     {
2372       // make or find baricentric nodes of faces
2373       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2374       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2375       {
2376         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2377           volFace2BaryNode.insert
2378           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2379         if ( !f_n->second )
2380         {
2381           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2382           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2383         }
2384         nodes.push_back( iF_n->second = f_n->second );
2385       }
2386     }
2387
2388     // make new volumes
2389     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2390     const int* volConn = splitMethod._connectivity;
2391     if ( splitMethod._nbCorners == 4 ) // tetra
2392       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2393         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2394                                                                nodes[ volConn[1] ],
2395                                                                nodes[ volConn[2] ],
2396                                                                nodes[ volConn[3] ]));
2397     else // prisms
2398       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2399         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2400                                                                nodes[ volConn[1] ],
2401                                                                nodes[ volConn[2] ],
2402                                                                nodes[ volConn[3] ],
2403                                                                nodes[ volConn[4] ],
2404                                                                nodes[ volConn[5] ]));
2405
2406     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2407
2408     // Split faces on sides of the split volume
2409
2410     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2411     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2412     {
2413       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2414       if ( nbNodes < 4 ) continue;
2415
2416       // find an existing face
2417       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2418                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2419       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2420                                                                        /*noMedium=*/false))
2421       {
2422         // make triangles
2423         helper.SetElementsOnShape( false );
2424         vector< const SMDS_MeshElement* > triangles;
2425
2426         // find submesh to add new triangles in
2427         if ( !fSubMesh || !fSubMesh->Contains( face ))
2428         {
2429           int shapeID = FindShape( face );
2430           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2431         }
2432         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2433         if ( iF_n != splitMethod._faceBaryNode.end() )
2434         {
2435           const SMDS_MeshNode *baryNode = iF_n->second;
2436           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2437           {
2438             const SMDS_MeshNode* n1 = fNodes[iN];
2439             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2440             const SMDS_MeshNode *n3 = baryNode;
2441             if ( !volTool.IsFaceExternal( iF ))
2442               swap( n2, n3 );
2443             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2444           }
2445           if ( fSubMesh ) // update position of the bary node on geometry
2446           {
2447             if ( subMesh )
2448               subMesh->RemoveNode( baryNode );
2449             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2450             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2451             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2452             {
2453               fHelper.SetSubShape( s );
2454               gp_XY uv( 1e100, 1e100 );
2455               double distXYZ[4];
2456               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2457                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2458                    uv.X() < 1e100 )
2459               {
2460                 // node is too far from the surface
2461                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2462                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2463                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2464               }
2465             }
2466           }
2467         }
2468         else
2469         {
2470           // among possible triangles create ones described by split method
2471           const int* nInd = volTool.GetFaceNodesIndices( iF );
2472           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2473           int iCom = 0; // common node of triangle faces to split into
2474           list< TTriangleFacet > facets;
2475           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2476           {
2477             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2478                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2479                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2480             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2481                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2482                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2483             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2484             {
2485               facets.push_back( t012 );
2486               facets.push_back( t023 );
2487               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2488                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2489                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2490                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2491               break;
2492             }
2493           }
2494           list< TTriangleFacet >::iterator facet = facets.begin();
2495           if ( facet == facets.end() )
2496             break;
2497           for ( ; facet != facets.end(); ++facet )
2498           {
2499             if ( !volTool.IsFaceExternal( iF ))
2500               swap( facet->_n2, facet->_n3 );
2501             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2502                                                  volNodes[ facet->_n2 ],
2503                                                  volNodes[ facet->_n3 ]));
2504           }
2505         }
2506         for ( size_t i = 0; i < triangles.size(); ++i )
2507         {
2508           if ( !triangles[ i ]) continue;
2509           if ( fSubMesh )
2510             fSubMesh->AddElement( triangles[ i ]);
2511           newElems.push_back( triangles[ i ]);
2512         }
2513         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2514         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2515
2516       } // while a face based on facet nodes exists
2517     } // loop on volume faces to split them into triangles
2518
2519     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2520
2521     if ( geomType == SMDSEntity_TriQuad_Hexa )
2522     {
2523       // remove medium nodes that could become free
2524       for ( int i = 20; i < volTool.NbNodes(); ++i )
2525         if ( volNodes[i]->NbInverseElements() == 0 )
2526           GetMeshDS()->RemoveNode( volNodes[i] );
2527     }
2528   } // loop on volumes to split
2529
2530   myLastCreatedNodes = newNodes;
2531   myLastCreatedElems = newElems;
2532 }
2533
2534 //=======================================================================
2535 //function : GetHexaFacetsToSplit
2536 //purpose  : For hexahedra that will be split into prisms, finds facets to
2537 //           split into triangles. Only hexahedra adjacent to the one closest
2538 //           to theFacetNormal.Location() are returned.
2539 //param [in,out] theHexas - the hexahedra
2540 //param [in]     theFacetNormal - facet normal
2541 //param [out]    theFacets - the hexahedra and found facet IDs
2542 //=======================================================================
2543
2544 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2545                                              const gp_Ax1&     theFacetNormal,
2546                                              TFacetOfElem &    theFacets)
2547 {
2548 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2549
2550   // Find a hexa closest to the location of theFacetNormal
2551
2552   const SMDS_MeshElement* startHex;
2553   {
2554     // get SMDS_ElemIteratorPtr on theHexas
2555     typedef const SMDS_MeshElement*                                      TValue;
2556     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2557     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2558     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2559     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2560     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2561       ( new TElemSetIter( theHexas.begin(),
2562                           theHexas.end(),
2563                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2564
2565     SMESH_ElementSearcher* searcher =
2566       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2567
2568     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2569
2570     delete searcher;
2571
2572     if ( !startHex )
2573       throw SALOME_Exception( THIS_METHOD "startHex not found");
2574   }
2575
2576   // Select a facet of startHex by theFacetNormal
2577
2578   SMDS_VolumeTool vTool( startHex );
2579   double norm[3], dot, maxDot = 0;
2580   int facetID = -1;
2581   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2582     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2583     {
2584       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2585       if ( dot > maxDot )
2586       {
2587         facetID = iF;
2588         maxDot = dot;
2589       }
2590     }
2591   if ( facetID < 0 )
2592     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2593
2594   // Fill theFacets starting from facetID of startHex
2595
2596   // facets used for searching of volumes adjacent to already treated ones
2597   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2598   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2599   TFacetMap facetsToCheck;
2600
2601   set<const SMDS_MeshNode*> facetNodes;
2602   const SMDS_MeshElement*   curHex;
2603
2604   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2605
2606   while ( startHex )
2607   {
2608     // move in two directions from startHex via facetID
2609     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2610     {
2611       curHex       = startHex;
2612       int curFacet = facetID;
2613       if ( is2nd ) // do not treat startHex twice
2614       {
2615         vTool.Set( curHex );
2616         if ( vTool.IsFreeFace( curFacet, &curHex ))
2617         {
2618           curHex = 0;
2619         }
2620         else
2621         {
2622           vTool.GetFaceNodes( curFacet, facetNodes );
2623           vTool.Set( curHex );
2624           curFacet = vTool.GetFaceIndex( facetNodes );
2625         }
2626       }
2627       while ( curHex )
2628       {
2629         // store a facet to split
2630         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2631         {
2632           theFacets.insert( make_pair( curHex, -1 ));
2633           break;
2634         }
2635         if ( !allHex && !theHexas.count( curHex ))
2636           break;
2637
2638         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2639           theFacets.insert( make_pair( curHex, curFacet ));
2640         if ( !facetIt2isNew.second )
2641           break;
2642
2643         // remember not-to-split facets in facetsToCheck
2644         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2645         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2646         {
2647           if ( iF == curFacet && iF == oppFacet )
2648             continue;
2649           TVolumeFaceKey facetKey ( vTool, iF );
2650           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2651           pair< TFacetMap::iterator, bool > it2isnew =
2652             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2653           if ( !it2isnew.second )
2654             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2655         }
2656         // pass to a volume adjacent via oppFacet
2657         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2658         {
2659           curHex = 0;
2660         }
2661         else
2662         {
2663           // get a new curFacet
2664           vTool.GetFaceNodes( oppFacet, facetNodes );
2665           vTool.Set( curHex );
2666           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2667         }
2668       }
2669     } // move in two directions from startHex via facetID
2670
2671     // Find a new startHex by facetsToCheck
2672
2673     startHex = 0;
2674     facetID  = -1;
2675     TFacetMap::iterator fIt = facetsToCheck.begin();
2676     while ( !startHex && fIt != facetsToCheck.end() )
2677     {
2678       const TElemFacets&  elemFacets = fIt->second;
2679       const SMDS_MeshElement*    hex = elemFacets.first->first;
2680       int                 splitFacet = elemFacets.first->second;
2681       int               lateralFacet = elemFacets.second;
2682       facetsToCheck.erase( fIt );
2683       fIt = facetsToCheck.begin();
2684
2685       vTool.Set( hex );
2686       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2687            curHex->GetGeomType() != SMDSGeom_HEXA )
2688         continue;
2689       if ( !allHex && !theHexas.count( curHex ))
2690         continue;
2691
2692       startHex = curHex;
2693
2694       // find a facet of startHex to split
2695
2696       set<const SMDS_MeshNode*> lateralNodes;
2697       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2698       vTool.GetFaceNodes( splitFacet,   facetNodes );
2699       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2700       vTool.Set( startHex );
2701       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2702
2703       // look for a facet of startHex having common nodes with facetNodes
2704       // but not lateralFacet
2705       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2706       {
2707         if ( iF == lateralFacet )
2708           continue;
2709         int nbCommonNodes = 0;
2710         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2711         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2712           nbCommonNodes += facetNodes.count( nn[ iN ]);
2713
2714         if ( nbCommonNodes >= 2 )
2715         {
2716           facetID = iF;
2717           break;
2718         }
2719       }
2720       if ( facetID < 0 )
2721         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2722     }
2723   } //   while ( startHex )
2724
2725   return;
2726 }
2727
2728 namespace
2729 {
2730   //================================================================================
2731   /*!
2732    * \brief Selects nodes of several elements according to a given interlace
2733    *  \param [in] srcNodes - nodes to select from
2734    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2735    *  \param [in] interlace - indices of nodes for all elements
2736    *  \param [in] nbElems - nb of elements
2737    *  \param [in] nbNodes - nb of nodes in each element
2738    *  \param [in] mesh - the mesh
2739    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2740    *  \param [in] type - type of elements to look for
2741    */
2742   //================================================================================
2743
2744   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2745                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2746                     const int*                            interlace,
2747                     const int                             nbElems,
2748                     const int                             nbNodes,
2749                     SMESHDS_Mesh*                         mesh = 0,
2750                     list< const SMDS_MeshElement* >*      elemQueue=0,
2751                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2752   {
2753     for ( int iE = 0; iE < nbElems; ++iE )
2754     {
2755       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2756       const int*                         select = & interlace[iE*nbNodes];
2757       elemNodes.resize( nbNodes );
2758       for ( int iN = 0; iN < nbNodes; ++iN )
2759         elemNodes[iN] = srcNodes[ select[ iN ]];
2760     }
2761     const SMDS_MeshElement* e;
2762     if ( elemQueue )
2763       for ( int iE = 0; iE < nbElems; ++iE )
2764         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2765           elemQueue->push_back( e );
2766   }
2767 }
2768
2769 //=======================================================================
2770 /*
2771  * Split bi-quadratic elements into linear ones without creation of additional nodes
2772  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2773  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2774  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2775  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2776  *   will be split in order to keep the mesh conformal.
2777  *  \param elems - elements to split
2778  */
2779 //=======================================================================
2780
2781 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2782 {
2783   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2784   vector<const SMDS_MeshElement* > splitElems;
2785   list< const SMDS_MeshElement* > elemQueue;
2786   list< const SMDS_MeshElement* >::iterator elemIt;
2787
2788   SMESHDS_Mesh * mesh = GetMeshDS();
2789   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2790   int nbElems, nbNodes;
2791
2792   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2793   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2794   {
2795     elemQueue.clear();
2796     elemQueue.push_back( *elemSetIt );
2797     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2798     {
2799       const SMDS_MeshElement* elem = *elemIt;
2800       switch( elem->GetEntityType() )
2801       {
2802       case SMDSEntity_TriQuad_Hexa: // HEX27
2803       {
2804         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2805         nbElems  = nbNodes = 8;
2806         elemType = & hexaType;
2807
2808         // get nodes for new elements
2809         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2810                                  { 1,9,20,8,    17,22,26,21 },
2811                                  { 2,10,20,9,   18,23,26,22 },
2812                                  { 3,11,20,10,  19,24,26,23 },
2813                                  { 16,21,26,24, 4,12,25,15  },
2814                                  { 17,22,26,21, 5,13,25,12  },
2815                                  { 18,23,26,22, 6,14,25,13  },
2816                                  { 19,24,26,23, 7,15,25,14  }};
2817         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2818
2819         // add boundary faces to elemQueue
2820         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2821                                  { 4,5,6,7, 12,13,14,15, 25 },
2822                                  { 0,1,5,4, 8,17,12,16,  21 },
2823                                  { 1,2,6,5, 9,18,13,17,  22 },
2824                                  { 2,3,7,6, 10,19,14,18, 23 },
2825                                  { 3,0,4,7, 11,16,15,19, 24 }};
2826         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2827
2828         // add boundary segments to elemQueue
2829         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2830                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2831                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2832         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2833         break;
2834       }
2835       case SMDSEntity_BiQuad_Triangle: // TRIA7
2836       {
2837         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2838         nbElems = 3;
2839         nbNodes = 4;
2840         elemType = & quadType;
2841
2842         // get nodes for new elements
2843         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2844         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2845
2846         // add boundary segments to elemQueue
2847         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2848         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2849         break;
2850       }
2851       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2852       {
2853         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2854         nbElems = 4;
2855         nbNodes = 4;
2856         elemType = & quadType;
2857
2858         // get nodes for new elements
2859         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2860         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2861
2862         // add boundary segments to elemQueue
2863         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2864         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2865         break;
2866       }
2867       case SMDSEntity_Quad_Edge:
2868       {
2869         if ( elemIt == elemQueue.begin() )
2870           continue; // an elem is in theElems
2871         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2872         nbElems = 2;
2873         nbNodes = 2;
2874         elemType = & segType;
2875
2876         // get nodes for new elements
2877         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2878         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2879         break;
2880       }
2881       default: continue;
2882       } // switch( elem->GetEntityType() )
2883
2884       // Create new elements
2885
2886       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2887
2888       splitElems.clear();
2889
2890       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2891       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2892       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2893       //elemType->SetID( -1 );
2894
2895       for ( int iE = 0; iE < nbElems; ++iE )
2896         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2897
2898
2899       ReplaceElemInGroups( elem, splitElems, mesh );
2900
2901       if ( subMesh )
2902         for ( size_t i = 0; i < splitElems.size(); ++i )
2903           subMesh->AddElement( splitElems[i] );
2904     }
2905   }
2906 }
2907
2908 //=======================================================================
2909 //function : AddToSameGroups
2910 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2911 //=======================================================================
2912
2913 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2914                                         const SMDS_MeshElement* elemInGroups,
2915                                         SMESHDS_Mesh *          aMesh)
2916 {
2917   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918   if (!groups.empty()) {
2919     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2920     for ( ; grIt != groups.end(); grIt++ ) {
2921       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2922       if ( group && group->Contains( elemInGroups ))
2923         group->SMDSGroup().Add( elemToAdd );
2924     }
2925   }
2926 }
2927
2928
2929 //=======================================================================
2930 //function : RemoveElemFromGroups
2931 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2932 //=======================================================================
2933 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2934                                              SMESHDS_Mesh *          aMesh)
2935 {
2936   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2937   if (!groups.empty())
2938   {
2939     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2940     for (; GrIt != groups.end(); GrIt++)
2941     {
2942       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2943       if (!grp || grp->IsEmpty()) continue;
2944       grp->SMDSGroup().Remove(removeelem);
2945     }
2946   }
2947 }
2948
2949 //================================================================================
2950 /*!
2951  * \brief Replace elemToRm by elemToAdd in the all groups
2952  */
2953 //================================================================================
2954
2955 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2956                                             const SMDS_MeshElement* elemToAdd,
2957                                             SMESHDS_Mesh *          aMesh)
2958 {
2959   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2960   if (!groups.empty()) {
2961     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2962     for ( ; grIt != groups.end(); grIt++ ) {
2963       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2964       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2965         group->SMDSGroup().Add( elemToAdd );
2966     }
2967   }
2968 }
2969
2970 //================================================================================
2971 /*!
2972  * \brief Replace elemToRm by elemToAdd in the all groups
2973  */
2974 //================================================================================
2975
2976 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2977                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2978                                             SMESHDS_Mesh *                         aMesh)
2979 {
2980   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2981   if (!groups.empty())
2982   {
2983     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2984     for ( ; grIt != groups.end(); grIt++ ) {
2985       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2986       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2987         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2988           group->SMDSGroup().Add( elemToAdd[ i ] );
2989     }
2990   }
2991 }
2992
2993 //=======================================================================
2994 //function : QuadToTri
2995 //purpose  : Cut quadrangles into triangles.
2996 //           theCrit is used to select a diagonal to cut
2997 //=======================================================================
2998
2999 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3000                                   const bool         the13Diag)
3001 {
3002   ClearLastCreated();
3003   myLastCreatedElems.reserve( theElems.size() * 2 );
3004
3005   SMESHDS_Mesh *       aMesh = GetMeshDS();
3006   Handle(Geom_Surface) surface;
3007   SMESH_MesherHelper   helper( *GetMesh() );
3008
3009   TIDSortedElemSet::iterator itElem;
3010   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3011   {
3012     const SMDS_MeshElement* elem = *itElem;
3013     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3014       continue;
3015
3016     if ( elem->NbNodes() == 4 ) {
3017       // retrieve element nodes
3018       const SMDS_MeshNode* aNodes [4];
3019       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3020       int i = 0;
3021       while ( itN->more() )
3022         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3023
3024       int aShapeId = FindShape( elem );
3025       const SMDS_MeshElement* newElem1 = 0;
3026       const SMDS_MeshElement* newElem2 = 0;
3027       if ( the13Diag ) {
3028         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3029         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3030       }
3031       else {
3032         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3033         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3034       }
3035       myLastCreatedElems.push_back(newElem1);
3036       myLastCreatedElems.push_back(newElem2);
3037       // put a new triangle on the same shape and add to the same groups
3038       if ( aShapeId )
3039       {
3040         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3041         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3042       }
3043       AddToSameGroups( newElem1, elem, aMesh );
3044       AddToSameGroups( newElem2, elem, aMesh );
3045       aMesh->RemoveElement( elem );
3046     }
3047
3048     // Quadratic quadrangle
3049
3050     else if ( elem->NbNodes() >= 8 )
3051     {
3052       // get surface elem is on
3053       int aShapeId = FindShape( elem );
3054       if ( aShapeId != helper.GetSubShapeID() ) {
3055         surface.Nullify();
3056         TopoDS_Shape shape;
3057         if ( aShapeId > 0 )
3058           shape = aMesh->IndexToShape( aShapeId );
3059         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3060           TopoDS_Face face = TopoDS::Face( shape );
3061           surface = BRep_Tool::Surface( face );
3062           if ( !surface.IsNull() )
3063             helper.SetSubShape( shape );
3064         }
3065       }
3066
3067       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3068       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3069       for ( int i = 0; itN->more(); ++i )
3070         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3071
3072       const SMDS_MeshNode* centrNode = aNodes[8];
3073       if ( centrNode == 0 )
3074       {
3075         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3077                                            surface.IsNull() );
3078         myLastCreatedNodes.push_back(centrNode);
3079       }
3080
3081       // create a new element
3082       const SMDS_MeshElement* newElem1 = 0;
3083       const SMDS_MeshElement* newElem2 = 0;
3084       if ( the13Diag ) {
3085         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3086                                   aNodes[6], aNodes[7], centrNode );
3087         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3088                                   centrNode, aNodes[4], aNodes[5] );
3089       }
3090       else {
3091         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3092                                   aNodes[7], aNodes[4], centrNode );
3093         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3094                                   centrNode, aNodes[5], aNodes[6] );
3095       }
3096       myLastCreatedElems.push_back(newElem1);
3097       myLastCreatedElems.push_back(newElem2);
3098       // put a new triangle on the same shape and add to the same groups
3099       if ( aShapeId )
3100       {
3101         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3102         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3103       }
3104       AddToSameGroups( newElem1, elem, aMesh );
3105       AddToSameGroups( newElem2, elem, aMesh );
3106       aMesh->RemoveElement( elem );
3107     }
3108   }
3109
3110   return true;
3111 }
3112
3113 //=======================================================================
3114 //function : getAngle
3115 //purpose  :
3116 //=======================================================================
3117
3118 double getAngle(const SMDS_MeshElement * tr1,
3119                 const SMDS_MeshElement * tr2,
3120                 const SMDS_MeshNode *    n1,
3121                 const SMDS_MeshNode *    n2)
3122 {
3123   double angle = 2. * M_PI; // bad angle
3124
3125   // get normals
3126   SMESH::Controls::TSequenceOfXYZ P1, P2;
3127   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3128        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3129     return angle;
3130   gp_Vec N1,N2;
3131   if(!tr1->IsQuadratic())
3132     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3133   else
3134     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3135   if ( N1.SquareMagnitude() <= gp::Resolution() )
3136     return angle;
3137   if(!tr2->IsQuadratic())
3138     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3139   else
3140     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3141   if ( N2.SquareMagnitude() <= gp::Resolution() )
3142     return angle;
3143
3144   // find the first diagonal node n1 in the triangles:
3145   // take in account a diagonal link orientation
3146   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3147   for ( int t = 0; t < 2; t++ ) {
3148     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3149     int i = 0, iDiag = -1;
3150     while ( it->more()) {
3151       const SMDS_MeshElement *n = it->next();
3152       if ( n == n1 || n == n2 ) {
3153         if ( iDiag < 0)
3154           iDiag = i;
3155         else {
3156           if ( i - iDiag == 1 )
3157             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3158           else
3159             nFirst[ t ] = n;
3160           break;
3161         }
3162       }
3163       i++;
3164     }
3165   }
3166   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3167     N2.Reverse();
3168
3169   angle = N1.Angle( N2 );
3170   //SCRUTE( angle );
3171   return angle;
3172 }
3173
3174 // =================================================
3175 // class generating a unique ID for a pair of nodes
3176 // and able to return nodes by that ID
3177 // =================================================
3178 class LinkID_Gen {
3179 public:
3180
3181   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3182     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3183   {}
3184
3185   smIdType GetLinkID (const SMDS_MeshNode * n1,
3186                   const SMDS_MeshNode * n2) const
3187   {
3188     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3189   }
3190
3191   bool GetNodes (const long             theLinkID,
3192                  const SMDS_MeshNode* & theNode1,
3193                  const SMDS_MeshNode* & theNode2) const
3194   {
3195     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3196     if ( !theNode1 ) return false;
3197     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3198     if ( !theNode2 ) return false;
3199     return true;
3200   }
3201
3202 private:
3203   LinkID_Gen();
3204   const SMESHDS_Mesh* myMesh;
3205   long                myMaxID;
3206 };
3207
3208
3209 //=======================================================================
3210 //function : TriToQuad
3211 //purpose  : Fuse neighbour triangles into quadrangles.
3212 //           theCrit is used to select a neighbour to fuse with.
3213 //           theMaxAngle is a max angle between element normals at which
3214 //           fusion is still performed.
3215 //=======================================================================
3216
3217 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3218                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3219                                   const double                         theMaxAngle)
3220 {
3221   ClearLastCreated();
3222   myLastCreatedElems.reserve( theElems.size() / 2 );
3223
3224   if ( !theCrit.get() )
3225     return false;
3226
3227   SMESHDS_Mesh * aMesh = GetMeshDS();
3228
3229   // Prepare data for algo: build
3230   // 1. map of elements with their linkIDs
3231   // 2. map of linkIDs with their elements
3232
3233   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3234   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3235   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3236   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3237
3238   TIDSortedElemSet::iterator itElem;
3239   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3240   {
3241     const SMDS_MeshElement* elem = *itElem;
3242     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3243     bool IsTria = ( elem->NbCornerNodes()==3 );
3244     if (!IsTria) continue;
3245
3246     // retrieve element nodes
3247     const SMDS_MeshNode* aNodes [4];
3248     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3249     int i = 0;
3250     while ( i < 3 )
3251       aNodes[ i++ ] = itN->next();
3252     aNodes[ 3 ] = aNodes[ 0 ];
3253
3254     // fill maps
3255     for ( i = 0; i < 3; i++ ) {
3256       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3257       // check if elements sharing a link can be fused
3258       itLE = mapLi_listEl.find( link );
3259       if ( itLE != mapLi_listEl.end() ) {
3260         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3261           continue;
3262         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3263         //if ( FindShape( elem ) != FindShape( elem2 ))
3264         //  continue; // do not fuse triangles laying on different shapes
3265         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3266           continue; // avoid making badly shaped quads
3267         (*itLE).second.push_back( elem );
3268       }
3269       else {
3270         mapLi_listEl[ link ].push_back( elem );
3271       }
3272       mapEl_setLi [ elem ].insert( link );
3273     }
3274   }
3275   // Clean the maps from the links shared by a sole element, ie
3276   // links to which only one element is bound in mapLi_listEl
3277
3278   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3279     int nbElems = (*itLE).second.size();
3280     if ( nbElems < 2  ) {
3281       const SMDS_MeshElement* elem = (*itLE).second.front();
3282       SMESH_TLink link = (*itLE).first;
3283       mapEl_setLi[ elem ].erase( link );
3284       if ( mapEl_setLi[ elem ].empty() )
3285         mapEl_setLi.erase( elem );
3286     }
3287   }
3288
3289   // Algo: fuse triangles into quadrangles
3290
3291   while ( ! mapEl_setLi.empty() ) {
3292     // Look for the start element:
3293     // the element having the least nb of shared links
3294     const SMDS_MeshElement* startElem = 0;
3295     int minNbLinks = 4;
3296     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3297       int nbLinks = (*itEL).second.size();
3298       if ( nbLinks < minNbLinks ) {
3299         startElem = (*itEL).first;
3300         minNbLinks = nbLinks;
3301         if ( minNbLinks == 1 )
3302           break;
3303       }
3304     }
3305
3306     // search elements to fuse starting from startElem or links of elements
3307     // fused earlyer - startLinks
3308     list< SMESH_TLink > startLinks;
3309     while ( startElem || !startLinks.empty() ) {
3310       while ( !startElem && !startLinks.empty() ) {
3311         // Get an element to start, by a link
3312         SMESH_TLink linkId = startLinks.front();
3313         startLinks.pop_front();
3314         itLE = mapLi_listEl.find( linkId );
3315         if ( itLE != mapLi_listEl.end() ) {
3316           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3317           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3318           for ( ; itE != listElem.end() ; itE++ )
3319             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3320               startElem = (*itE);
3321           mapLi_listEl.erase( itLE );
3322         }
3323       }
3324
3325       if ( startElem ) {
3326         // Get candidates to be fused
3327         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3328         const SMESH_TLink *link12 = 0, *link13 = 0;
3329         startElem = 0;
3330         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3331         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3332         ASSERT( !setLi.empty() );
3333         set< SMESH_TLink >::iterator itLi;
3334         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3335         {
3336           const SMESH_TLink & link = (*itLi);
3337           itLE = mapLi_listEl.find( link );
3338           if ( itLE == mapLi_listEl.end() )
3339             continue;
3340
3341           const SMDS_MeshElement* elem = (*itLE).second.front();
3342           if ( elem == tr1 )
3343             elem = (*itLE).second.back();
3344           mapLi_listEl.erase( itLE );
3345           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3346             continue;
3347           if ( tr2 ) {
3348             tr3 = elem;
3349             link13 = &link;
3350           }
3351           else {
3352             tr2 = elem;
3353             link12 = &link;
3354           }
3355
3356           // add other links of elem to list of links to re-start from
3357           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3358           set< SMESH_TLink >::iterator it;
3359           for ( it = links.begin(); it != links.end(); it++ ) {
3360             const SMESH_TLink& link2 = (*it);
3361             if ( link2 != link )
3362               startLinks.push_back( link2 );
3363           }
3364         }
3365
3366         // Get nodes of possible quadrangles
3367         const SMDS_MeshNode *n12 [4], *n13 [4];
3368         bool Ok12 = false, Ok13 = false;
3369         const SMDS_MeshNode *linkNode1, *linkNode2;
3370         if(tr2) {
3371           linkNode1 = link12->first;
3372           linkNode2 = link12->second;
3373           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3374             Ok12 = true;
3375         }
3376         if(tr3) {
3377           linkNode1 = link13->first;
3378           linkNode2 = link13->second;
3379           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3380             Ok13 = true;
3381         }
3382
3383         // Choose a pair to fuse
3384         if ( Ok12 && Ok13 ) {
3385           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3386           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3387           double aBadRate12 = getBadRate( &quad12, theCrit );
3388           double aBadRate13 = getBadRate( &quad13, theCrit );
3389           if (  aBadRate13 < aBadRate12 )
3390             Ok12 = false;
3391           else
3392             Ok13 = false;
3393         }
3394
3395         // Make quadrangles
3396         // and remove fused elems and remove links from the maps
3397         mapEl_setLi.erase( tr1 );
3398         if ( Ok12 )
3399         {
3400           mapEl_setLi.erase( tr2 );
3401           mapLi_listEl.erase( *link12 );
3402           if ( tr1->NbNodes() == 3 )
3403           {
3404             const SMDS_MeshElement* newElem = 0;
3405             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3406             myLastCreatedElems.push_back(newElem);
3407             AddToSameGroups( newElem, tr1, aMesh );
3408             int aShapeId = tr1->getshapeId();
3409             if ( aShapeId )
3410               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3411             aMesh->RemoveElement( tr1 );
3412             aMesh->RemoveElement( tr2 );
3413           }
3414           else {
3415             vector< const SMDS_MeshNode* > N1;
3416             vector< const SMDS_MeshNode* > N2;
3417             getNodesFromTwoTria(tr1,tr2,N1,N2);
3418             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3419             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3420             // i.e. first nodes from both arrays form a new diagonal
3421             const SMDS_MeshNode* aNodes[8];
3422             aNodes[0] = N1[0];
3423             aNodes[1] = N1[1];
3424             aNodes[2] = N2[0];
3425             aNodes[3] = N2[1];
3426             aNodes[4] = N1[3];
3427             aNodes[5] = N2[5];
3428             aNodes[6] = N2[3];
3429             aNodes[7] = N1[5];
3430             const SMDS_MeshElement* newElem = 0;
3431             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3432               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3433                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3434             else
3435               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3436                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3437             myLastCreatedElems.push_back(newElem);
3438             AddToSameGroups( newElem, tr1, aMesh );
3439             int aShapeId = tr1->getshapeId();
3440             if ( aShapeId )
3441               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3442             aMesh->RemoveElement( tr1 );
3443             aMesh->RemoveElement( tr2 );
3444             // remove middle node (9)
3445             if ( N1[4]->NbInverseElements() == 0 )
3446               aMesh->RemoveNode( N1[4] );
3447             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3448               aMesh->RemoveNode( N1[6] );
3449             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3450               aMesh->RemoveNode( N2[6] );
3451           }
3452         }
3453         else if ( Ok13 )
3454         {
3455           mapEl_setLi.erase( tr3 );
3456           mapLi_listEl.erase( *link13 );
3457           if ( tr1->NbNodes() == 3 ) {
3458             const SMDS_MeshElement* newElem = 0;
3459             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3460             myLastCreatedElems.push_back(newElem);
3461             AddToSameGroups( newElem, tr1, aMesh );
3462             int aShapeId = tr1->getshapeId();
3463             if ( aShapeId )
3464               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3465             aMesh->RemoveElement( tr1 );
3466             aMesh->RemoveElement( tr3 );
3467           }
3468           else {
3469             vector< const SMDS_MeshNode* > N1;
3470             vector< const SMDS_MeshNode* > N2;
3471             getNodesFromTwoTria(tr1,tr3,N1,N2);
3472             // now we receive following N1 and N2 (using numeration as above image)
3473             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3474             // i.e. first nodes from both arrays form a new diagonal
3475             const SMDS_MeshNode* aNodes[8];
3476             aNodes[0] = N1[0];
3477             aNodes[1] = N1[1];
3478             aNodes[2] = N2[0];
3479             aNodes[3] = N2[1];
3480             aNodes[4] = N1[3];
3481             aNodes[5] = N2[5];
3482             aNodes[6] = N2[3];
3483             aNodes[7] = N1[5];
3484             const SMDS_MeshElement* newElem = 0;
3485             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3486               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3487                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3488             else
3489               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3490                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3491             myLastCreatedElems.push_back(newElem);
3492             AddToSameGroups( newElem, tr1, aMesh );
3493             int aShapeId = tr1->getshapeId();
3494             if ( aShapeId )
3495               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3496             aMesh->RemoveElement( tr1 );
3497             aMesh->RemoveElement( tr3 );
3498             // remove middle node (9)
3499             if ( N1[4]->NbInverseElements() == 0 )
3500               aMesh->RemoveNode( N1[4] );
3501             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3502               aMesh->RemoveNode( N1[6] );
3503             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3504               aMesh->RemoveNode( N2[6] );
3505           }
3506         }
3507
3508         // Next element to fuse: the rejected one
3509         if ( tr3 )
3510           startElem = Ok12 ? tr3 : tr2;
3511
3512       } // if ( startElem )
3513     } // while ( startElem || !startLinks.empty() )
3514   } // while ( ! mapEl_setLi.empty() )
3515
3516   return true;
3517 }
3518
3519 //================================================================================
3520 /*!
3521  * \brief Return nodes linked to the given one
3522  * \param theNode - the node
3523  * \param linkedNodes - the found nodes
3524  * \param type - the type of elements to check
3525  *
3526  * Medium nodes are ignored
3527  */
3528 //================================================================================
3529
3530 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3531                                        TIDSortedElemSet &   linkedNodes,
3532                                        SMDSAbs_ElementType  type )
3533 {
3534   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3535   while ( elemIt->more() )
3536   {
3537     const SMDS_MeshElement* elem = elemIt->next();
3538     if(elem->GetType() == SMDSAbs_0DElement)
3539       continue;
3540
3541     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3542     if ( elem->GetType() == SMDSAbs_Volume )
3543     {
3544       SMDS_VolumeTool vol( elem );
3545       while ( nodeIt->more() ) {
3546         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3547         if ( theNode != n && vol.IsLinked( theNode, n ))
3548           linkedNodes.insert( n );
3549       }
3550     }
3551     else
3552     {
3553       for ( int i = 0; nodeIt->more(); ++i ) {
3554         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3555         if ( n == theNode ) {
3556           int iBefore = i - 1;
3557           int iAfter  = i + 1;
3558           if ( elem->IsQuadratic() ) {
3559             int nb = elem->NbNodes() / 2;
3560             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3561             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3562           }
3563           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3564           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3565         }
3566       }
3567     }
3568   }
3569 }
3570
3571 //=======================================================================
3572 //function : laplacianSmooth
3573 //purpose  : pulls theNode toward the center of surrounding nodes directly
3574 //           connected to that node along an element edge
3575 //=======================================================================
3576
3577 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3578                      const Handle(Geom_Surface)&          theSurface,
3579                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3580 {
3581   // find surrounding nodes
3582
3583   TIDSortedElemSet nodeSet;
3584   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3585
3586   // compute new coodrs
3587
3588   double coord[] = { 0., 0., 0. };
3589   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3590   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3591     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3592     if ( theSurface.IsNull() ) { // smooth in 3D
3593       coord[0] += node->X();
3594       coord[1] += node->Y();
3595       coord[2] += node->Z();
3596     }
3597     else { // smooth in 2D
3598       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3599       gp_XY* uv = theUVMap[ node ];
3600       coord[0] += uv->X();
3601       coord[1] += uv->Y();
3602     }
3603   }
3604   int nbNodes = nodeSet.size();
3605   if ( !nbNodes )
3606     return;
3607   coord[0] /= nbNodes;
3608   coord[1] /= nbNodes;
3609
3610   if ( !theSurface.IsNull() ) {
3611     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3612     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3613     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3614     coord[0] = p3d.X();
3615     coord[1] = p3d.Y();
3616     coord[2] = p3d.Z();
3617   }
3618   else
3619     coord[2] /= nbNodes;
3620
3621   // move node
3622
3623   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3624 }
3625
3626 //=======================================================================
3627 //function : centroidalSmooth
3628 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3629 //           surrounding elements
3630 //=======================================================================
3631
3632 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3633                       const Handle(Geom_Surface)&          theSurface,
3634                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3635 {
3636   gp_XYZ aNewXYZ(0.,0.,0.);
3637   SMESH::Controls::Area anAreaFunc;
3638   double totalArea = 0.;
3639   int nbElems = 0;
3640
3641   // compute new XYZ
3642
3643   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3644   while ( elemIt->more() )
3645   {
3646     const SMDS_MeshElement* elem = elemIt->next();
3647     nbElems++;
3648
3649     gp_XYZ elemCenter(0.,0.,0.);
3650     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3651     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3652     int nn = elem->NbNodes();
3653     if(elem->IsQuadratic()) nn = nn/2;
3654     int i=0;
3655     //while ( itN->more() ) {
3656     while ( i<nn ) {
3657       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3658       i++;
3659       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3660       aNodePoints.push_back( aP );
3661       if ( !theSurface.IsNull() ) { // smooth in 2D
3662         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3663         gp_XY* uv = theUVMap[ aNode ];
3664         aP.SetCoord( uv->X(), uv->Y(), 0. );
3665       }
3666       elemCenter += aP;
3667     }
3668     double elemArea = anAreaFunc.GetValue( aNodePoints );
3669     totalArea += elemArea;
3670     elemCenter /= nn;
3671     aNewXYZ += elemCenter * elemArea;
3672   }
3673   aNewXYZ /= totalArea;
3674   if ( !theSurface.IsNull() ) {
3675     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3676     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3677   }
3678
3679   // move node
3680
3681   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3682 }
3683
3684 //=======================================================================
3685 //function : getClosestUV
3686 //purpose  : return UV of closest projection
3687 //=======================================================================
3688
3689 static bool getClosestUV (Extrema_GenExtPS& projector,
3690                           const gp_Pnt&     point,
3691                           gp_XY &           result)
3692 {
3693   projector.Perform( point );
3694   if ( projector.IsDone() ) {
3695     double u = 0, v = 0, minVal = DBL_MAX;
3696     for ( int i = projector.NbExt(); i > 0; i-- )
3697       if ( projector.SquareDistance( i ) < minVal ) {
3698         minVal = projector.SquareDistance( i );
3699         projector.Point( i ).Parameter( u, v );
3700       }
3701     result.SetCoord( u, v );
3702     return true;
3703   }
3704   return false;
3705 }
3706
3707 //=======================================================================
3708 //function : Smooth
3709 //purpose  : Smooth theElements during theNbIterations or until a worst
3710 //           element has aspect ratio <= theTgtAspectRatio.
3711 //           Aspect Ratio varies in range [1.0, inf].
3712 //           If theElements is empty, the whole mesh is smoothed.
3713 //           theFixedNodes contains additionally fixed nodes. Nodes built
3714 //           on edges and boundary nodes are always fixed.
3715 //=======================================================================
3716
3717 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3718                                set<const SMDS_MeshNode*> & theFixedNodes,
3719                                const SmoothMethod          theSmoothMethod,
3720                                const int                   theNbIterations,
3721                                double                      theTgtAspectRatio,
3722                                const bool                  the2D)
3723 {
3724   ClearLastCreated();
3725
3726   if ( theTgtAspectRatio < 1.0 )
3727     theTgtAspectRatio = 1.0;
3728
3729   const double disttol = 1.e-16;
3730
3731   SMESH::Controls::AspectRatio aQualityFunc;
3732
3733   SMESHDS_Mesh* aMesh = GetMeshDS();
3734
3735   if ( theElems.empty() ) {
3736     // add all faces to theElems
3737     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3738     while ( fIt->more() ) {
3739       const SMDS_MeshElement* face = fIt->next();
3740       theElems.insert( theElems.end(), face );
3741     }
3742   }
3743   // get all face ids theElems are on
3744   set< int > faceIdSet;
3745   TIDSortedElemSet::iterator itElem;
3746   if ( the2D )
3747     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3748       int fId = FindShape( *itElem );
3749       // check that corresponding submesh exists and a shape is face
3750       if (fId &&
3751           faceIdSet.find( fId ) == faceIdSet.end() &&
3752           aMesh->MeshElements( fId )) {
3753         TopoDS_Shape F = aMesh->IndexToShape( fId );
3754         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3755           faceIdSet.insert( fId );
3756       }
3757     }
3758   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3759
3760   // ===============================================
3761   // smooth elements on each TopoDS_Face separately
3762   // ===============================================
3763
3764   SMESH_MesherHelper helper( *GetMesh() );
3765
3766   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3767   for ( ; fId != faceIdSet.rend(); ++fId )
3768   {
3769     // get face surface and submesh
3770     Handle(Geom_Surface) surface;
3771     SMESHDS_SubMesh* faceSubMesh = 0;
3772     TopoDS_Face face;
3773     double fToler2 = 0;
3774     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3775     bool isUPeriodic = false, isVPeriodic = false;
3776     if ( *fId )
3777     {
3778       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3779       surface = BRep_Tool::Surface( face );
3780       faceSubMesh = aMesh->MeshElements( *fId );
3781       fToler2 = BRep_Tool::Tolerance( face );
3782       fToler2 *= fToler2 * 10.;
3783       isUPeriodic = surface->IsUPeriodic();
3784       // if ( isUPeriodic )
3785       //   surface->UPeriod();
3786       isVPeriodic = surface->IsVPeriodic();
3787       // if ( isVPeriodic )
3788       //   surface->VPeriod();
3789       surface->Bounds( u1, u2, v1, v2 );
3790       helper.SetSubShape( face );
3791     }
3792     // ---------------------------------------------------------
3793     // for elements on a face, find movable and fixed nodes and
3794     // compute UV for them
3795     // ---------------------------------------------------------
3796     bool checkBoundaryNodes = false;
3797     bool isQuadratic = false;
3798     set<const SMDS_MeshNode*> setMovableNodes;
3799     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3800     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3801     list< const SMDS_MeshElement* > elemsOnFace;
3802
3803     Extrema_GenExtPS projector;
3804     GeomAdaptor_Surface surfAdaptor;
3805     if ( !surface.IsNull() ) {
3806       surfAdaptor.Load( surface );
3807       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3808     }
3809     int nbElemOnFace = 0;
3810     itElem = theElems.begin();
3811     // loop on not yet smoothed elements: look for elems on a face
3812     while ( itElem != theElems.end() )
3813     {
3814       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3815         break; // all elements found
3816
3817       const SMDS_MeshElement* elem = *itElem;
3818       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3819            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3820         ++itElem;
3821         continue;
3822       }
3823       elemsOnFace.push_back( elem );
3824       theElems.erase( itElem++ );
3825       nbElemOnFace++;
3826
3827       if ( !isQuadratic )
3828         isQuadratic = elem->IsQuadratic();
3829
3830       // get movable nodes of elem
3831       const SMDS_MeshNode* node;
3832       SMDS_TypeOfPosition posType;
3833       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3834       int nn = 0, nbn =  elem->NbNodes();
3835       if(elem->IsQuadratic())
3836         nbn = nbn/2;
3837       while ( nn++ < nbn ) {
3838         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3839         const SMDS_PositionPtr& pos = node->GetPosition();
3840         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3841         if (posType != SMDS_TOP_EDGE &&
3842             posType != SMDS_TOP_VERTEX &&
3843             theFixedNodes.find( node ) == theFixedNodes.end())
3844         {
3845           // check if all faces around the node are on faceSubMesh
3846           // because a node on edge may be bound to face
3847           bool all = true;
3848           if ( faceSubMesh ) {
3849             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3850             while ( eIt->more() && all ) {
3851               const SMDS_MeshElement* e = eIt->next();
3852               all = faceSubMesh->Contains( e );
3853             }
3854           }
3855           if ( all )
3856             setMovableNodes.insert( node );
3857           else
3858             checkBoundaryNodes = true;
3859         }
3860         if ( posType == SMDS_TOP_3DSPACE )
3861           checkBoundaryNodes = true;
3862       }
3863
3864       if ( surface.IsNull() )
3865         continue;
3866
3867       // get nodes to check UV
3868       list< const SMDS_MeshNode* > uvCheckNodes;
3869       const SMDS_MeshNode* nodeInFace = 0;
3870       itN = elem->nodesIterator();
3871       nn = 0; nbn =  elem->NbNodes();
3872       if(elem->IsQuadratic())
3873         nbn = nbn/2;
3874       while ( nn++ < nbn ) {
3875         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876         if ( node->GetPosition()->GetDim() == 2 )
3877           nodeInFace = node;
3878         if ( uvMap.find( node ) == uvMap.end() )
3879           uvCheckNodes.push_back( node );
3880         // add nodes of elems sharing node
3881         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3882         //         while ( eIt->more() ) {
3883         //           const SMDS_MeshElement* e = eIt->next();
3884         //           if ( e != elem ) {
3885         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3886         //             while ( nIt->more() ) {
3887         //               const SMDS_MeshNode* n =
3888         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3889         //               if ( uvMap.find( n ) == uvMap.end() )
3890         //                 uvCheckNodes.push_back( n );
3891         //             }
3892         //           }
3893         //         }
3894       }
3895       // check UV on face
3896       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3897       for ( ; n != uvCheckNodes.end(); ++n ) {
3898         node = *n;
3899         gp_XY uv( 0, 0 );
3900         const SMDS_PositionPtr& pos = node->GetPosition();
3901         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3902         // get existing UV
3903         if ( pos )
3904         {
3905           bool toCheck = true;
3906           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3907         }
3908         // compute not existing UV
3909         bool project = ( posType == SMDS_TOP_3DSPACE );
3910         // double dist1 = DBL_MAX, dist2 = 0;
3911         // if ( posType != SMDS_TOP_3DSPACE ) {
3912         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3913         //   project = dist1 > fToler2;
3914         // }
3915         if ( project ) { // compute new UV
3916           gp_XY newUV;
3917           gp_Pnt pNode = SMESH_NodeXYZ( node );
3918           if ( !getClosestUV( projector, pNode, newUV )) {
3919             MESSAGE("Node Projection Failed " << node);
3920           }
3921           else {
3922             if ( isUPeriodic )
3923               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3924             if ( isVPeriodic )
3925               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3926             // check new UV
3927             // if ( posType != SMDS_TOP_3DSPACE )
3928             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3929             // if ( dist2 < dist1 )
3930             uv = newUV;
3931           }
3932         }
3933         // store UV in the map
3934         listUV.push_back( uv );
3935         uvMap.insert( make_pair( node, &listUV.back() ));
3936       }
3937     } // loop on not yet smoothed elements
3938
3939     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3940       checkBoundaryNodes = true;
3941
3942     // fix nodes on mesh boundary
3943
3944     if ( checkBoundaryNodes ) {
3945       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3946       map< SMESH_TLink, int >::iterator link_nb;
3947       // put all elements links to linkNbMap
3948       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3949       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3950         const SMDS_MeshElement* elem = (*elemIt);
3951         int nbn =  elem->NbCornerNodes();
3952         // loop on elem links: insert them in linkNbMap
3953         for ( int iN = 0; iN < nbn; ++iN ) {
3954           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3955           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3956           SMESH_TLink link( n1, n2 );
3957           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3958           link_nb->second++;
3959         }
3960       }
3961       // remove nodes that are in links encountered only once from setMovableNodes
3962       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3963         if ( link_nb->second == 1 ) {
3964           setMovableNodes.erase( link_nb->first.node1() );
3965           setMovableNodes.erase( link_nb->first.node2() );
3966         }
3967       }
3968     }
3969
3970     // -----------------------------------------------------
3971     // for nodes on seam edge, compute one more UV ( uvMap2 );
3972     // find movable nodes linked to nodes on seam and which
3973     // are to be smoothed using the second UV ( uvMap2 )
3974     // -----------------------------------------------------
3975
3976     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3977     if ( !surface.IsNull() ) {
3978       TopExp_Explorer eExp( face, TopAbs_EDGE );
3979       for ( ; eExp.More(); eExp.Next() ) {
3980         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3981         if ( !BRep_Tool::IsClosed( edge, face ))
3982           continue;
3983         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3984         if ( !sm ) continue;
3985         // find out which parameter varies for a node on seam
3986         double f,l;
3987         gp_Pnt2d uv1, uv2;
3988         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3989         if ( pcurve.IsNull() ) continue;
3990         uv1 = pcurve->Value( f );
3991         edge.Reverse();
3992         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3993         if ( pcurve.IsNull() ) continue;
3994         uv2 = pcurve->Value( f );
3995         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3996         // assure uv1 < uv2
3997         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3998           std::swap( uv1, uv2 );
3999         // get nodes on seam and its vertices
4000         list< const SMDS_MeshNode* > seamNodes;
4001         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4002         while ( nSeamIt->more() ) {
4003           const SMDS_MeshNode* node = nSeamIt->next();
4004           if ( !isQuadratic || !IsMedium( node ))
4005             seamNodes.push_back( node );
4006         }
4007         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4008         for ( ; vExp.More(); vExp.Next() ) {
4009           sm = aMesh->MeshElements( vExp.Current() );
4010           if ( sm ) {
4011             nSeamIt = sm->GetNodes();
4012             while ( nSeamIt->more() )
4013               seamNodes.push_back( nSeamIt->next() );
4014           }
4015         }
4016         // loop on nodes on seam
4017         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4018         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4019           const SMDS_MeshNode* nSeam = *noSeIt;
4020           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4021           if ( n_uv == uvMap.end() )
4022             continue;
4023           // set the first UV
4024           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4025           // set the second UV
4026           listUV.push_back( *n_uv->second );
4027           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4028           if ( uvMap2.empty() )
4029             uvMap2 = uvMap; // copy the uvMap contents
4030           uvMap2[ nSeam ] = &listUV.back();
4031
4032           // collect movable nodes linked to ones on seam in nodesNearSeam
4033           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4034           while ( eIt->more() ) {
4035             const SMDS_MeshElement* e = eIt->next();
4036             int nbUseMap1 = 0, nbUseMap2 = 0;
4037             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4038             int nn = 0, nbn =  e->NbNodes();
4039             if(e->IsQuadratic()) nbn = nbn/2;
4040             while ( nn++ < nbn )
4041             {
4042               const SMDS_MeshNode* n =
4043                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4044               if (n == nSeam ||
4045                   setMovableNodes.find( n ) == setMovableNodes.end() )
4046                 continue;
4047               // add only nodes being closer to uv2 than to uv1
4048               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4049               //              0.5 * ( n->Y() + nSeam->Y() ),
4050               //              0.5 * ( n->Z() + nSeam->Z() ));
4051               // gp_XY uv;
4052               // getClosestUV( projector, pMid, uv );
4053               double x = uvMap[ n ]->Coord( iPar );
4054               if ( Abs( uv1.Coord( iPar ) - x ) >
4055                    Abs( uv2.Coord( iPar ) - x )) {
4056                 nodesNearSeam.insert( n );
4057                 nbUseMap2++;
4058               }
4059               else
4060                 nbUseMap1++;
4061             }
4062             // for centroidalSmooth all element nodes must
4063             // be on one side of a seam
4064             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4065               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4066               nn = 0;
4067               while ( nn++ < nbn ) {
4068                 const SMDS_MeshNode* n =
4069                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4070                 setMovableNodes.erase( n );
4071               }
4072             }
4073           }
4074         } // loop on nodes on seam
4075       } // loop on edge of a face
4076     } // if ( !face.IsNull() )
4077
4078     if ( setMovableNodes.empty() ) {
4079       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4080       continue; // goto next face
4081     }
4082
4083     // -------------
4084     // SMOOTHING //
4085     // -------------
4086
4087     int it = -1;
4088     double maxRatio = -1., maxDisplacement = -1.;
4089     set<const SMDS_MeshNode*>::iterator nodeToMove;
4090     for ( it = 0; it < theNbIterations; it++ ) {
4091       maxDisplacement = 0.;
4092       nodeToMove = setMovableNodes.begin();
4093       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4094         const SMDS_MeshNode* node = (*nodeToMove);
4095         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4096
4097         // smooth
4098         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4099         if ( theSmoothMethod == LAPLACIAN )
4100           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4101         else
4102           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4103
4104         // node displacement
4105         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4106         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4107         if ( aDispl > maxDisplacement )
4108           maxDisplacement = aDispl;
4109       }
4110       // no node movement => exit
4111       //if ( maxDisplacement < 1.e-16 ) {
4112       if ( maxDisplacement < disttol ) {
4113         MESSAGE("-- no node movement --");
4114         break;
4115       }
4116
4117       // check elements quality
4118       maxRatio  = 0;
4119       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4120       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4121         const SMDS_MeshElement* elem = (*elemIt);
4122         if ( !elem || elem->GetType() != SMDSAbs_Face )
4123           continue;
4124         SMESH::Controls::TSequenceOfXYZ aPoints;
4125         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4126           double aValue = aQualityFunc.GetValue( aPoints );
4127           if ( aValue > maxRatio )
4128             maxRatio = aValue;
4129         }
4130       }
4131       if ( maxRatio <= theTgtAspectRatio ) {
4132         //MESSAGE("-- quality achieved --");
4133         break;
4134       }
4135       if (it+1 == theNbIterations) {
4136         //MESSAGE("-- Iteration limit exceeded --");
4137       }
4138     } // smoothing iterations
4139
4140     // MESSAGE(" Face id: " << *fId <<
4141     //         " Nb iterstions: " << it <<
4142     //         " Displacement: " << maxDisplacement <<
4143     //         " Aspect Ratio " << maxRatio);
4144
4145     // ---------------------------------------
4146     // new nodes positions are computed,
4147     // record movement in DS and set new UV
4148     // ---------------------------------------
4149     nodeToMove = setMovableNodes.begin();
4150     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4151       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4152       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4153       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4154       if ( node_uv != uvMap.end() ) {
4155         gp_XY* uv = node_uv->second;
4156         node->SetPosition
4157           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4158       }
4159     }
4160
4161     // move medium nodes of quadratic elements
4162     if ( isQuadratic )
4163     {
4164       vector<const SMDS_MeshNode*> nodes;
4165       bool checkUV;
4166       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4167       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4168       {
4169         const SMDS_MeshElement* QF = *elemIt;
4170         if ( QF->IsQuadratic() )
4171         {
4172           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4173                         SMDS_MeshElement::iterator() );
4174           nodes.push_back( nodes[0] );
4175           gp_Pnt xyz;
4176           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4177           {
4178             if ( !surface.IsNull() )
4179             {
4180               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4181               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4182               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4183               xyz = surface->Value( uv.X(), uv.Y() );
4184             }
4185             else {
4186               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4187             }
4188             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4189               // we have to move a medium node
4190               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4191           }
4192         }
4193       }
4194     }
4195
4196   } // loop on face ids
4197
4198 }
4199
4200 namespace
4201 {
4202   //=======================================================================
4203   //function : isReverse
4204   //purpose  : Return true if normal of prevNodes is not co-directied with
4205   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4206   //           iNotSame is where prevNodes and nextNodes are different.
4207   //           If result is true then future volume orientation is OK
4208   //=======================================================================
4209
4210   bool isReverse(const SMDS_MeshElement*             face,
4211                  const vector<const SMDS_MeshNode*>& prevNodes,
4212                  const vector<const SMDS_MeshNode*>& nextNodes,
4213                  const int                           iNotSame)
4214   {
4215
4216     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4217     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4218     gp_XYZ extrDir( pN - pP ), faceNorm;
4219     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4220
4221     return faceNorm * extrDir < 0.0;
4222   }
4223
4224   //================================================================================
4225   /*!
4226    * \brief Assure that theElemSets[0] holds elements, not nodes
4227    */
4228   //================================================================================
4229
4230   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4231   {
4232     if ( !theElemSets[0].empty() &&
4233          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4234     {
4235       std::swap( theElemSets[0], theElemSets[1] );
4236     }
4237     else if ( !theElemSets[1].empty() &&
4238               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4239     {
4240       std::swap( theElemSets[0], theElemSets[1] );
4241     }
4242   }
4243 }
4244
4245 //=======================================================================
4246 /*!
4247  * \brief Create elements by sweeping an element
4248  * \param elem - element to sweep
4249  * \param newNodesItVec - nodes generated from each node of the element
4250  * \param newElems - generated elements
4251  * \param nbSteps - number of sweeping steps
4252  * \param srcElements - to append elem for each generated element
4253  */
4254 //=======================================================================
4255
4256 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4257                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4258                                     list<const SMDS_MeshElement*>&        newElems,
4259                                     const size_t                          nbSteps,
4260                                     SMESH_SequenceOfElemPtr&              srcElements)
4261 {
4262   SMESHDS_Mesh* aMesh = GetMeshDS();
4263
4264   const int           nbNodes = elem->NbNodes();
4265   const int         nbCorners = elem->NbCornerNodes();
4266   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4267                                                           polyhedron creation !!! */
4268   // Loop on elem nodes:
4269   // find new nodes and detect same nodes indices
4270   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4271   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4272   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4273   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4274
4275   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4276   vector<int> sames(nbNodes);
4277   vector<bool> isSingleNode(nbNodes);
4278
4279   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4280     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4281     const SMDS_MeshNode*                         node = nnIt->first;
4282     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4283     if ( listNewNodes.empty() )
4284       return;
4285
4286     itNN   [ iNode ] = listNewNodes.begin();
4287     prevNod[ iNode ] = node;
4288     nextNod[ iNode ] = listNewNodes.front();
4289
4290     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4291                                                              corner node of linear */
4292     if ( prevNod[ iNode ] != nextNod [ iNode ])
4293       nbDouble += !isSingleNode[iNode];
4294
4295     if( iNode < nbCorners ) { // check corners only
4296       if ( prevNod[ iNode ] == nextNod [ iNode ])
4297         sames[nbSame++] = iNode;
4298       else
4299         iNotSameNode = iNode;
4300     }
4301   }
4302
4303   if ( nbSame == nbNodes || nbSame > 2) {
4304     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4305     return;
4306   }
4307
4308   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4309   {
4310     // fix nodes order to have bottom normal external
4311     if ( baseType == SMDSEntity_Polygon )
4312     {
4313       std::reverse( itNN.begin(), itNN.end() );
4314       std::reverse( prevNod.begin(), prevNod.end() );
4315       std::reverse( midlNod.begin(), midlNod.end() );
4316       std::reverse( nextNod.begin(), nextNod.end() );
4317       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4318     }
4319     else
4320     {
4321       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4322       SMDS_MeshCell::applyInterlace( ind, itNN );
4323       SMDS_MeshCell::applyInterlace( ind, prevNod );
4324       SMDS_MeshCell::applyInterlace( ind, nextNod );
4325       SMDS_MeshCell::applyInterlace( ind, midlNod );
4326       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4327       if ( nbSame > 0 )
4328       {
4329         sames[nbSame] = iNotSameNode;
4330         for ( int j = 0; j <= nbSame; ++j )
4331           for ( size_t i = 0; i < ind.size(); ++i )
4332             if ( ind[i] == sames[j] )
4333             {
4334               sames[j] = i;
4335               break;
4336             }
4337         iNotSameNode = sames[nbSame];
4338       }
4339     }
4340   }
4341   else if ( elem->GetType() == SMDSAbs_Edge )
4342   {
4343     // orient a new face same as adjacent one
4344     int i1, i2;
4345     const SMDS_MeshElement* e;
4346     TIDSortedElemSet dummy;
4347     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4348         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4349         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4350     {
4351       // there is an adjacent face, check order of nodes in it
4352       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4353       if ( sameOrder )
4354       {
4355         std::swap( itNN[0],    itNN[1] );
4356         std::swap( prevNod[0], prevNod[1] );
4357         std::swap( nextNod[0], nextNod[1] );
4358         std::swap( isSingleNode[0], isSingleNode[1] );
4359         if ( nbSame > 0 )
4360           sames[0] = 1 - sames[0];
4361         iNotSameNode = 1 - iNotSameNode;
4362       }
4363     }
4364   }
4365
4366   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4367   if ( nbSame > 0 ) {
4368     iSameNode    = sames[ nbSame-1 ];
4369     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4370     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4371     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4372   }
4373
4374   if ( baseType == SMDSEntity_Polygon )
4375   {
4376     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4377     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4378   }
4379   else if ( baseType == SMDSEntity_Quad_Polygon )
4380   {
4381     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4382     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4383   }
4384
4385   // make new elements
4386   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4387   {
4388     // get next nodes
4389     for ( iNode = 0; iNode < nbNodes; iNode++ )
4390     {
4391       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4392       nextNod[ iNode ] = *itNN[ iNode ]++;
4393     }
4394
4395     SMDS_MeshElement* aNewElem = 0;
4396     /*if(!elem->IsPoly())*/ {
4397       switch ( baseType ) {
4398       case SMDSEntity_0D:
4399       case SMDSEntity_Node: { // sweep NODE
4400         if ( nbSame == 0 ) {
4401           if ( isSingleNode[0] )
4402             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4403           else
4404             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4405         }
4406         else
4407           return;
4408         break;
4409       }
4410       case SMDSEntity_Edge: { // sweep EDGE
4411         if ( nbDouble == 0 )
4412         {
4413           if ( nbSame == 0 ) // ---> quadrangle
4414             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4415                                       nextNod[ 1 ], nextNod[ 0 ] );
4416           else               // ---> triangle
4417             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4418                                       nextNod[ iNotSameNode ] );
4419         }
4420         else                 // ---> polygon
4421         {
4422           vector<const SMDS_MeshNode*> poly_nodes;
4423           poly_nodes.push_back( prevNod[0] );
4424           poly_nodes.push_back( prevNod[1] );
4425           if ( prevNod[1] != nextNod[1] )
4426           {
4427             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4428             poly_nodes.push_back( nextNod[1] );
4429           }
4430           if ( prevNod[0] != nextNod[0] )
4431           {
4432             poly_nodes.push_back( nextNod[0] );
4433             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4434           }
4435           switch ( poly_nodes.size() ) {
4436           case 3:
4437             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4438             break;
4439           case 4:
4440             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4441                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4442             break;
4443           default:
4444             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4445           }
4446         }
4447         break;
4448       }
4449       case SMDSEntity_Triangle: // TRIANGLE --->
4450       {
4451         if ( nbDouble > 0 ) break;
4452         if ( nbSame == 0 )       // ---> pentahedron
4453           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4454                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4455
4456         else if ( nbSame == 1 )  // ---> pyramid
4457           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4458                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4459                                        nextNod[ iSameNode ]);
4460
4461         else // 2 same nodes:       ---> tetrahedron
4462           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4463                                        nextNod[ iNotSameNode ]);
4464         break;
4465       }
4466       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4467       {
4468         if ( nbSame == 2 )
4469           return;
4470         if ( nbDouble+nbSame == 2 )
4471         {
4472           if(nbSame==0) {      // ---> quadratic quadrangle
4473             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4474                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4475           }
4476           else { //(nbSame==1) // ---> quadratic triangle
4477             if(sames[0]==2) {
4478               return; // medium node on axis
4479             }
4480             else if(sames[0]==0)
4481               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4482                                         prevNod[2], midlNod[1], nextNod[2] );
4483             else // sames[0]==1
4484               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4485                                         prevNod[2], nextNod[2], midlNod[0]);
4486           }
4487         }
4488         else if ( nbDouble == 3 )
4489         {
4490           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4491             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4492                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4493           }
4494         }
4495         else
4496           return;
4497         break;
4498       }
4499       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4500         if ( nbDouble > 0 ) break;
4501
4502         if ( nbSame == 0 )       // ---> hexahedron
4503           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4504                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4505
4506         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4507           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4508                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4509                                        nextNod[ iSameNode ]);
4510           newElems.push_back( aNewElem );
4511           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4512                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4513                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4514         }
4515         else if ( nbSame == 2 ) { // ---> pentahedron
4516           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4517             // iBeforeSame is same too
4518             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4519                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4520                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4521           else
4522             // iAfterSame is same too
4523             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4524                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4525                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4526         }
4527         break;
4528       }
4529       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4530       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4531         if ( nbDouble+nbSame != 3 ) break;
4532         if(nbSame==0) {
4533           // --->  pentahedron with 15 nodes
4534           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4535                                        nextNod[0], nextNod[1], nextNod[2],
4536                                        prevNod[3], prevNod[4], prevNod[5],
4537                                        nextNod[3], nextNod[4], nextNod[5],
4538                                        midlNod[0], midlNod[1], midlNod[2]);
4539         }
4540         else if(nbSame==1) {
4541           // --->  2d order pyramid of 13 nodes
4542           int apex = iSameNode;
4543           int i0 = ( apex + 1 ) % nbCorners;
4544           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4545           int i0a = apex + 3;
4546           int i1a = i1 + 3;
4547           int i01 = i0 + 3;
4548           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4549                                       nextNod[i0], nextNod[i1], prevNod[apex],
4550                                       prevNod[i01], midlNod[i0],
4551                                       nextNod[i01], midlNod[i1],
4552                                       prevNod[i1a], prevNod[i0a],
4553                                       nextNod[i0a], nextNod[i1a]);
4554         }
4555         else if(nbSame==2) {
4556           // --->  2d order tetrahedron of 10 nodes
4557           int n1 = iNotSameNode;
4558           int n2 = ( n1 + 1             ) % nbCorners;
4559           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4560           int n12 = n1 + 3;
4561           int n23 = n2 + 3;
4562           int n31 = n3 + 3;
4563           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4564                                        prevNod[n12], prevNod[n23], prevNod[n31],
4565                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4566         }
4567         break;
4568       }
4569       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4570         if( nbSame == 0 ) {
4571           if ( nbDouble != 4 ) break;
4572           // --->  hexahedron with 20 nodes
4573           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4574                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4575                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4576                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4577                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4578         }
4579         else if(nbSame==1) {
4580           // ---> pyramid + pentahedron - can not be created since it is needed
4581           // additional middle node at the center of face
4582           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4583           return;
4584         }
4585         else if( nbSame == 2 ) {
4586           if ( nbDouble != 2 ) break;
4587           // --->  2d order Pentahedron with 15 nodes
4588           int n1,n2,n4,n5;
4589           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4590             // iBeforeSame is same too
4591             n1 = iBeforeSame;
4592             n2 = iOpposSame;
4593             n4 = iSameNode;
4594             n5 = iAfterSame;
4595           }
4596           else {
4597             // iAfterSame is same too
4598             n1 = iSameNode;
4599             n2 = iBeforeSame;
4600             n4 = iAfterSame;
4601             n5 = iOpposSame;
4602           }
4603           int n12 = n2 + 4;
4604           int n45 = n4 + 4;
4605           int n14 = n1 + 4;
4606           int n25 = n5 + 4;
4607           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4608                                        prevNod[n4], prevNod[n5], nextNod[n5],
4609                                        prevNod[n12], midlNod[n2], nextNod[n12],
4610                                        prevNod[n45], midlNod[n5], nextNod[n45],
4611                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4612         }
4613         break;
4614       }
4615       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4616
4617         if( nbSame == 0 && nbDouble == 9 ) {
4618           // --->  tri-quadratic hexahedron with 27 nodes
4619           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4620                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4621                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4622                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4623                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4624                                        prevNod[8], // bottom center
4625                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4626                                        nextNod[8], // top center
4627                                        midlNod[8]);// elem center
4628         }
4629         else
4630         {
4631           return;
4632         }
4633         break;
4634       }
4635       case SMDSEntity_Polygon: { // sweep POLYGON
4636
4637         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4638           // --->  hexagonal prism
4639           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4640                                        prevNod[3], prevNod[4], prevNod[5],
4641                                        nextNod[0], nextNod[1], nextNod[2],
4642                                        nextNod[3], nextNod[4], nextNod[5]);
4643         }
4644         break;
4645       }
4646       case SMDSEntity_Ball:
4647         return;
4648
4649       default:
4650         break;
4651       } // switch ( baseType )
4652     } // scope
4653
4654     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4655     {
4656       if ( baseType != SMDSEntity_Polygon )
4657       {
4658         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4659         SMDS_MeshCell::applyInterlace( ind, prevNod );
4660         SMDS_MeshCell::applyInterlace( ind, nextNod );
4661         SMDS_MeshCell::applyInterlace( ind, midlNod );
4662         SMDS_MeshCell::applyInterlace( ind, itNN );
4663         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4664         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4665       }
4666       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4667       vector<int> quantities (nbNodes + 2);
4668       polyedre_nodes.clear();
4669       quantities.clear();
4670
4671       // bottom of prism
4672       for (int inode = 0; inode < nbNodes; inode++)
4673         polyedre_nodes.push_back( prevNod[inode] );
4674       quantities.push_back( nbNodes );
4675
4676       // top of prism
4677       polyedre_nodes.push_back( nextNod[0] );
4678       for (int inode = nbNodes; inode-1; --inode )
4679         polyedre_nodes.push_back( nextNod[inode-1] );
4680       quantities.push_back( nbNodes );
4681
4682       // side faces
4683       // 3--6--2
4684       // |     |
4685       // 7     5
4686       // |     |
4687       // 0--4--1
4688       const int iQuad = elem->IsQuadratic();
4689       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4690       {
4691         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4692         int inextface = (iface+1+iQuad) % nbNodes;
4693         int imid      = (iface+1) % nbNodes;
4694         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4695         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4696         polyedre_nodes.push_back( prevNod[iface] );             // 1
4697         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4698         {
4699           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4700           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4701         }
4702         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4703         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4704         {
4705           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4706           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4707         }
4708         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4709         if ( nbFaceNodes > 2 )
4710           quantities.push_back( nbFaceNodes );
4711         else // degenerated face
4712           polyedre_nodes.resize( prevNbNodes );
4713       }
4714       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4715
4716     } // try to create a polyherdal prism
4717
4718     if ( aNewElem ) {
4719       newElems.push_back( aNewElem );
4720       myLastCreatedElems.push_back(aNewElem);
4721       srcElements.push_back( elem );
4722     }
4723
4724     // set new prev nodes
4725     for ( iNode = 0; iNode < nbNodes; iNode++ )
4726       prevNod[ iNode ] = nextNod[ iNode ];
4727
4728   } // loop on steps
4729 }
4730
4731 //=======================================================================
4732 /*!
4733  * \brief Create 1D and 2D elements around swept elements
4734  * \param mapNewNodes - source nodes and ones generated from them
4735  * \param newElemsMap - source elements and ones generated from them
4736  * \param elemNewNodesMap - nodes generated from each node of each element
4737  * \param elemSet - all swept elements
4738  * \param nbSteps - number of sweeping steps
4739  * \param srcElements - to append elem for each generated element
4740  */
4741 //=======================================================================
4742
4743 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4744                                   TTElemOfElemListMap &    newElemsMap,
4745                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4746                                   TIDSortedElemSet&        elemSet,
4747                                   const int                nbSteps,
4748                                   SMESH_SequenceOfElemPtr& srcElements)
4749 {
4750   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4751   SMESHDS_Mesh* aMesh = GetMeshDS();
4752
4753   // Find nodes belonging to only one initial element - sweep them into edges.
4754
4755   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4756   for ( ; nList != mapNewNodes.end(); nList++ )
4757   {
4758     const SMDS_MeshNode* node =
4759       static_cast<const SMDS_MeshNode*>( nList->first );
4760     if ( newElemsMap.count( node ))
4761       continue; // node was extruded into edge
4762     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4763     int nbInitElems = 0;
4764     const SMDS_MeshElement* el = 0;
4765     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4766     while ( eIt->more() && nbInitElems < 2 ) {
4767       const SMDS_MeshElement* e = eIt->next();
4768       SMDSAbs_ElementType  type = e->GetType();
4769       if ( type == SMDSAbs_Volume ||
4770            type < highType ||
4771            !elemSet.count(e))
4772         continue;
4773       if ( type > highType ) {
4774         nbInitElems = 0;
4775         highType    = type;
4776       }
4777       el = e;
4778       ++nbInitElems;
4779     }
4780     if ( nbInitElems == 1 ) {
4781       bool NotCreateEdge = el && el->IsMediumNode(node);
4782       if(!NotCreateEdge) {
4783         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4784         list<const SMDS_MeshElement*> newEdges;
4785         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4786       }
4787     }
4788   }
4789
4790   // Make a ceiling for each element ie an equal element of last new nodes.
4791   // Find free links of faces - make edges and sweep them into faces.
4792
4793   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4794
4795   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4796   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4797   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4798   {
4799     const SMDS_MeshElement* elem = itElem->first;
4800     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4801
4802     if(itElem->second.size()==0) continue;
4803
4804     const bool isQuadratic = elem->IsQuadratic();
4805
4806     if ( elem->GetType() == SMDSAbs_Edge ) {
4807       // create a ceiling edge
4808       if ( !isQuadratic ) {
4809         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4810                                vecNewNodes[ 1 ]->second.back())) {
4811           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4812                                                       vecNewNodes[ 1 ]->second.back()));
4813           srcElements.push_back( elem );
4814         }
4815       }
4816       else {
4817         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4818                                vecNewNodes[ 1 ]->second.back(),
4819                                vecNewNodes[ 2 ]->second.back())) {
4820           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4821                                                       vecNewNodes[ 1 ]->second.back(),
4822                                                       vecNewNodes[ 2 ]->second.back()));
4823           srcElements.push_back( elem );
4824         }
4825       }
4826     }
4827     if ( elem->GetType() != SMDSAbs_Face )
4828       continue;
4829
4830     bool hasFreeLinks = false;
4831
4832     TIDSortedElemSet avoidSet;
4833     avoidSet.insert( elem );
4834
4835     set<const SMDS_MeshNode*> aFaceLastNodes;
4836     int iNode, nbNodes = vecNewNodes.size();
4837     if ( !isQuadratic ) {
4838       // loop on the face nodes
4839       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4840         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4841         // look for free links of the face
4842         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4843         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4844         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4845         // check if a link n1-n2 is free
4846         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4847           hasFreeLinks = true;
4848           // make a new edge and a ceiling for a new edge
4849           const SMDS_MeshElement* edge;
4850           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4851             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4852             srcElements.push_back( myLastCreatedElems.back() );
4853           }
4854           n1 = vecNewNodes[ iNode ]->second.back();
4855           n2 = vecNewNodes[ iNext ]->second.back();
4856           if ( !aMesh->FindEdge( n1, n2 )) {
4857             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4858             srcElements.push_back( edge );
4859           }
4860         }
4861       }
4862     }
4863     else { // elem is quadratic face
4864       int nbn = nbNodes/2;
4865       for ( iNode = 0; iNode < nbn; iNode++ ) {
4866         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4867         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4868         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4869         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4870         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4871         // check if a link is free
4872         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4873              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4874              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4875           hasFreeLinks = true;
4876           // make an edge and a ceiling for a new edge
4877           // find medium node
4878           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4879             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4880             srcElements.push_back( elem );
4881           }
4882           n1 = vecNewNodes[ iNode ]->second.back();
4883           n2 = vecNewNodes[ iNext ]->second.back();
4884           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4885           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4886             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4887             srcElements.push_back( elem );
4888           }
4889         }
4890       }
4891       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4892         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4893       }
4894     }
4895
4896     // sweep free links into faces
4897
4898     if ( hasFreeLinks ) {
4899       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4900       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4901
4902       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4903       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4904       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4905         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4906         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4907       }
4908       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4909         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4910         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4911       }
4912       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4913         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4914         std::advance( v, volNb );
4915         // find indices of free faces of a volume and their source edges
4916         list< int > freeInd;
4917         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4918         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4919         int iF, nbF = vTool.NbFaces();
4920         for ( iF = 0; iF < nbF; iF ++ ) {
4921           if ( vTool.IsFreeFace( iF ) &&
4922                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4923                initNodeSet != faceNodeSet) // except an initial face
4924           {
4925             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4926               continue;
4927             if ( faceNodeSet == initNodeSetNoCenter )
4928               continue;
4929             freeInd.push_back( iF );
4930             // find source edge of a free face iF
4931             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4932             vector<const SMDS_MeshNode*>::iterator lastCommom;
4933             commonNodes.resize( nbNodes, 0 );
4934             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4935                                                 initNodeSet.begin(), initNodeSet.end(),
4936                                                 commonNodes.begin());
4937             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4938               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4939             else
4940               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4941 #ifdef _DEBUG_
4942             if ( !srcEdges.back() )
4943             {
4944               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4945                    << iF << " of volume #" << vTool.ID() << endl;
4946             }
4947 #endif
4948           }
4949         }
4950         if ( freeInd.empty() )
4951           continue;
4952
4953         // create wall faces for all steps;
4954         // if such a face has been already created by sweep of edge,
4955         // assure that its orientation is OK
4956         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4957         {
4958           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4959           vTool.SetExternalNormal();
4960           const int nextShift = vTool.IsForward() ? +1 : -1;
4961           list< int >::iterator ind = freeInd.begin();
4962           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4963           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4964           {
4965             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4966             int nbn = vTool.NbFaceNodes( *ind );
4967             const SMDS_MeshElement * f = 0;
4968             if ( nbn == 3 )              ///// triangle
4969             {
4970               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4971               if ( !f ||
4972                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4973               {
4974                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4975                                                      nodes[ 1 ],
4976                                                      nodes[ 1 + nextShift ] };
4977                 if ( f )
4978                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4979                 else
4980                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4981                                                                newOrder[ 2 ] ));
4982               }
4983             }
4984             else if ( nbn == 4 )       ///// quadrangle
4985             {
4986               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4987               if ( !f ||
4988                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4989               {
4990                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4991                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4992                 if ( f )
4993                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4994                 else
4995                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4996                                                                newOrder[ 2 ], newOrder[ 3 ]));
4997               }
4998             }
4999             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5000             {
5001               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5002               if ( !f ||
5003                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5004               {
5005                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5006                                                      nodes[2],
5007                                                      nodes[2 + 2*nextShift],
5008                                                      nodes[3 - 2*nextShift],
5009                                                      nodes[3],
5010                                                      nodes[3 + 2*nextShift]};
5011                 if ( f )
5012                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5013                 else
5014                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5015                                                                newOrder[ 1 ],
5016                                                                newOrder[ 2 ],
5017                                                                newOrder[ 3 ],
5018                                                                newOrder[ 4 ],
5019                                                                newOrder[ 5 ] ));
5020               }
5021             }
5022             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5023             {
5024               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5025                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5026               if ( !f ||
5027                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5028               {
5029                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5030                                                      nodes[4 - 2*nextShift],
5031                                                      nodes[4],
5032                                                      nodes[4 + 2*nextShift],
5033                                                      nodes[1],
5034                                                      nodes[5 - 2*nextShift],
5035                                                      nodes[5],
5036                                                      nodes[5 + 2*nextShift] };
5037                 if ( f )
5038                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5039                 else
5040                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5041                                                               newOrder[ 2 ], newOrder[ 3 ],
5042                                                               newOrder[ 4 ], newOrder[ 5 ],
5043                                                               newOrder[ 6 ], newOrder[ 7 ]));
5044               }
5045             }
5046             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5047             {
5048               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5049                                       SMDSAbs_Face, /*noMedium=*/false);
5050               if ( !f ||
5051                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5052               {
5053                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5054                                                      nodes[4 - 2*nextShift],
5055                                                      nodes[4],
5056                                                      nodes[4 + 2*nextShift],
5057                                                      nodes[1],
5058                                                      nodes[5 - 2*nextShift],
5059                                                      nodes[5],
5060                                                      nodes[5 + 2*nextShift],
5061                                                      nodes[8] };
5062                 if ( f )
5063                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5064                 else
5065                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5066                                                               newOrder[ 2 ], newOrder[ 3 ],
5067                                                               newOrder[ 4 ], newOrder[ 5 ],
5068                                                               newOrder[ 6 ], newOrder[ 7 ],
5069                                                               newOrder[ 8 ]));
5070               }
5071             }
5072             else  //////// polygon
5073             {
5074               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5075               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5076               if ( !f ||
5077                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5078               {
5079                 if ( !vTool.IsForward() )
5080                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5081                 if ( f )
5082                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5083                 else
5084                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5085               }
5086             }
5087
5088             while ( srcElements.size() < myLastCreatedElems.size() )
5089               srcElements.push_back( *srcEdge );
5090
5091           }  // loop on free faces
5092
5093           // go to the next volume
5094           iVol = 0;
5095           while ( iVol++ < nbVolumesByStep ) v++;
5096
5097         } // loop on steps
5098       } // loop on volumes of one step
5099     } // sweep free links into faces
5100
5101     // Make a ceiling face with a normal external to a volume
5102
5103     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5104     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5105     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5106
5107     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5108       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5109       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5110     }
5111     if ( iF >= 0 )
5112     {
5113       lastVol.SetExternalNormal();
5114       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5115       const               int nbn = lastVol.NbFaceNodes( iF );
5116       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5117       if ( !hasFreeLinks ||
5118            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5119       {
5120         const vector<int>& interlace =
5121           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5122         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5123
5124         AddElement( nodeVec, anyFace.Init( elem ));
5125
5126         while ( srcElements.size() < myLastCreatedElems.size() )
5127           srcElements.push_back( elem );
5128       }
5129     }
5130   } // loop on swept elements
5131 }
5132
5133 //=======================================================================
5134 //function : RotationSweep
5135 //purpose  :
5136 //=======================================================================
5137
5138 SMESH_MeshEditor::PGroupIDs
5139 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5140                                 const gp_Ax1&      theAxis,
5141                                 const double       theAngle,
5142                                 const int          theNbSteps,
5143                                 const double       theTol,
5144                                 const bool         theMakeGroups,
5145                                 const bool         theMakeWalls)
5146 {
5147   ClearLastCreated();
5148
5149   setElemsFirst( theElemSets );
5150   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5151   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5152
5153   // source elements for each generated one
5154   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5155   srcElems.reserve( theElemSets[0].size() );
5156   srcNodes.reserve( theElemSets[1].size() );
5157
5158   gp_Trsf aTrsf;
5159   aTrsf.SetRotation( theAxis, theAngle );
5160   gp_Trsf aTrsf2;
5161   aTrsf2.SetRotation( theAxis, theAngle/2. );
5162
5163   gp_Lin aLine( theAxis );
5164   double aSqTol = theTol * theTol;
5165
5166   SMESHDS_Mesh* aMesh = GetMeshDS();
5167
5168   TNodeOfNodeListMap mapNewNodes;
5169   TElemOfVecOfNnlmiMap mapElemNewNodes;
5170   TTElemOfElemListMap newElemsMap;
5171
5172   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5173                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5174                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5175   // loop on theElemSets
5176   TIDSortedElemSet::iterator itElem;
5177   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5178   {
5179     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5180     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5181       const SMDS_MeshElement* elem = *itElem;
5182       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5183         continue;
5184       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5185       newNodesItVec.reserve( elem->NbNodes() );
5186
5187       // loop on elem nodes
5188       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5189       while ( itN->more() )
5190       {
5191         const SMDS_MeshNode* node = cast2Node( itN->next() );
5192
5193         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5194         double coord[3];
5195         aXYZ.Coord( coord[0], coord[1], coord[2] );
5196         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5197
5198         // check if a node has been already sweeped
5199         TNodeOfNodeListMapItr nIt =
5200           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5201         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5202         if ( listNewNodes.empty() )
5203         {
5204           // check if we are to create medium nodes between corner ones
5205           bool needMediumNodes = false;
5206           if ( isQuadraticMesh )
5207           {
5208             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5209             while (it->more() && !needMediumNodes )
5210             {
5211               const SMDS_MeshElement* invElem = it->next();
5212               if ( invElem != elem && !theElems.count( invElem )) continue;
5213               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5214               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5215                 needMediumNodes = true;
5216             }
5217           }
5218
5219           // make new nodes
5220           const SMDS_MeshNode * newNode = node;
5221           for ( int i = 0; i < theNbSteps; i++ ) {
5222             if ( !isOnAxis ) {
5223               if ( needMediumNodes )  // create a medium node
5224               {
5225                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5226                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5227                 myLastCreatedNodes.push_back(newNode);
5228                 srcNodes.push_back( node );
5229                 listNewNodes.push_back( newNode );
5230                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5231               }
5232               else {
5233                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5234               }
5235               // create a corner node
5236               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5237               myLastCreatedNodes.push_back(newNode);
5238               srcNodes.push_back( node );
5239               listNewNodes.push_back( newNode );
5240             }
5241             else {
5242               listNewNodes.push_back( newNode );
5243               // if ( needMediumNodes )
5244               //   listNewNodes.push_back( newNode );
5245             }
5246           }
5247         }
5248         newNodesItVec.push_back( nIt );
5249       }
5250       // make new elements
5251       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5252     }
5253   }
5254
5255   if ( theMakeWalls )
5256     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5257
5258   PGroupIDs newGroupIDs;
5259   if ( theMakeGroups )
5260     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5261
5262   return newGroupIDs;
5263 }
5264
5265 //=======================================================================
5266 //function : ExtrusParam
5267 //purpose  : standard construction
5268 //=======================================================================
5269
5270 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5271                                             const int                theNbSteps,
5272                                             const std::list<double>& theScales,
5273                                             const std::list<double>& theAngles,
5274                                             const gp_XYZ*            theBasePoint,
5275                                             const int                theFlags,
5276                                             const double             theTolerance):
5277   myDir( theStep ),
5278   myBaseP( Precision::Infinite(), 0, 0 ),
5279   myFlags( theFlags ),
5280   myTolerance( theTolerance ),
5281   myElemsToUse( NULL )
5282 {
5283   mySteps = new TColStd_HSequenceOfReal;
5284   const double stepSize = theStep.Magnitude();
5285   for (int i=1; i<=theNbSteps; i++ )
5286     mySteps->Append( stepSize );
5287
5288   if ( !theScales.empty() )
5289   {
5290     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5291       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5292
5293     // add medium scales
5294     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5295     myScales.reserve( theNbSteps * 2 );
5296     myScales.push_back( 0.5 * ( *s1 + 1. ));
5297     myScales.push_back( *s1 );
5298     for ( ; s2 != theScales.end(); s1 = s2++ )
5299     {
5300       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5301       myScales.push_back( *s2 );
5302     }
5303   }
5304
5305   if ( !theAngles.empty() )
5306   {
5307     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5308     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5309       linearAngleVariation( theNbSteps, angles );
5310
5311     // accumulate angles
5312     double angle = 0;
5313     int nbAngles = 0;
5314     std::list<double>::iterator a1 = angles.begin(), a2;
5315     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5316     {
5317       angle += *a1;
5318       *a1 = angle;
5319     }
5320     while ( nbAngles++ < theNbSteps )
5321       angles.push_back( angles.back() );
5322
5323     // add medium angles
5324     a2 = angles.begin(), a1 = a2++;
5325     myAngles.push_back( 0.5 * *a1 );
5326     myAngles.push_back( *a1 );
5327     for ( ; a2 != angles.end(); a1 = a2++ )
5328     {
5329       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5330       myAngles.push_back( *a2 );
5331     }
5332   }
5333
5334   if ( theBasePoint )
5335   {
5336     myBaseP = *theBasePoint;
5337   }
5338
5339   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5340       ( theTolerance > 0 ))
5341   {
5342     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5343   }
5344   else
5345   {
5346     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5347   }
5348 }
5349
5350 //=======================================================================
5351 //function : ExtrusParam
5352 //purpose  : steps are given explicitly
5353 //=======================================================================
5354
5355 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5356                                             Handle(TColStd_HSequenceOfReal) theSteps,
5357                                             const int                       theFlags,
5358                                             const double                    theTolerance):
5359   myDir( theDir ),
5360   mySteps( theSteps ),
5361   myFlags( theFlags ),
5362   myTolerance( theTolerance ),
5363   myElemsToUse( NULL )
5364 {
5365   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5366       ( theTolerance > 0 ))
5367   {
5368     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5369   }
5370   else
5371   {
5372     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5373   }
5374 }
5375
5376 //=======================================================================
5377 //function : ExtrusParam
5378 //purpose  : for extrusion by normal
5379 //=======================================================================
5380
5381 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5382                                             const int    theNbSteps,
5383                                             const int    theFlags,
5384                                             const int    theDim ):
5385   myDir( 1,0,0 ),
5386   mySteps( new TColStd_HSequenceOfReal ),
5387   myFlags( theFlags ),
5388   myTolerance( 0 ),
5389   myElemsToUse( NULL )
5390 {
5391   for (int i = 0; i < theNbSteps; i++ )
5392     mySteps->Append( theStepSize );
5393
5394   if ( theDim == 1 )
5395   {
5396     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5397   }
5398   else
5399   {
5400     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5401   }
5402 }
5403
5404 //=======================================================================
5405 //function : ExtrusParam
5406 //purpose  : for extrusion along path
5407 //=======================================================================
5408
5409 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5410                                             const gp_Pnt*                   theBasePoint,
5411                                             const std::list<double>&        theScales,
5412                                             const bool                      theMakeGroups )
5413   : myBaseP( Precision::Infinite(), 0, 0 ),
5414     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5415     myPathPoints( thePoints )
5416 {
5417   if ( theBasePoint )
5418   {
5419     myBaseP = theBasePoint->XYZ();
5420   }
5421
5422   if ( !theScales.empty() )
5423   {
5424     // add medium scales
5425     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5426     myScales.reserve( thePoints.size() * 2 );
5427     myScales.push_back( 0.5 * ( 1. + *s1 ));
5428     myScales.push_back( *s1 );
5429     for ( ; s2 != theScales.end(); s1 = s2++ )
5430     {
5431       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5432       myScales.push_back( *s2 );
5433     }
5434   }
5435
5436   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5437 }
5438
5439 //=======================================================================
5440 //function : ExtrusParam::SetElementsToUse
5441 //purpose  : stores elements to use for extrusion by normal, depending on
5442 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5443 //           define myBaseP for scaling
5444 //=======================================================================
5445
5446 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5447                                                       const TIDSortedElemSet& nodes )
5448 {
5449   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5450
5451   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5452   {
5453     myBaseP.SetCoord( 0.,0.,0. );
5454     TIDSortedElemSet newNodes;
5455
5456     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5457     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5458     {
5459       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5460       TIDSortedElemSet::const_iterator itElem = elements.begin();
5461       for ( ; itElem != elements.end(); itElem++ )
5462       {
5463         const SMDS_MeshElement* elem = *itElem;
5464         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5465         while ( itN->more() ) {
5466           const SMDS_MeshElement* node = itN->next();
5467           if ( newNodes.insert( node ).second )
5468             myBaseP += SMESH_NodeXYZ( node );
5469         }
5470       }
5471     }
5472     myBaseP /= newNodes.size();
5473   }
5474 }
5475
5476 //=======================================================================
5477 //function : ExtrusParam::beginStepIter
5478 //purpose  : prepare iteration on steps
5479 //=======================================================================
5480
5481 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5482 {
5483   myWithMediumNodes = withMediumNodes;
5484   myNextStep = 1;
5485   myCurSteps.clear();
5486 }
5487 //=======================================================================
5488 //function : ExtrusParam::moreSteps
5489 //purpose  : are there more steps?
5490 //=======================================================================
5491
5492 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5493 {
5494   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5495 }
5496 //=======================================================================
5497 //function : ExtrusParam::nextStep
5498 //purpose  : returns the next step
5499 //=======================================================================
5500
5501 double SMESH_MeshEditor::ExtrusParam::nextStep()
5502 {
5503   double res = 0;
5504   if ( !myCurSteps.empty() )
5505   {
5506     res = myCurSteps.back();
5507     myCurSteps.pop_back();
5508   }
5509   else if ( myNextStep <= mySteps->Length() )
5510   {
5511     myCurSteps.push_back( mySteps->Value( myNextStep ));
5512     ++myNextStep;
5513     if ( myWithMediumNodes )
5514     {
5515       myCurSteps.back() /= 2.;
5516       myCurSteps.push_back( myCurSteps.back() );
5517     }
5518     res = nextStep();
5519   }
5520   return res;
5521 }
5522
5523 //=======================================================================
5524 //function : ExtrusParam::makeNodesByDir
5525 //purpose  : create nodes for standard extrusion
5526 //=======================================================================
5527
5528 int SMESH_MeshEditor::ExtrusParam::
5529 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5530                 const SMDS_MeshNode*              srcNode,
5531                 std::list<const SMDS_MeshNode*> & newNodes,
5532                 const bool                        makeMediumNodes)
5533 {
5534   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5535
5536   int nbNodes = 0;
5537   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5538   {
5539     p += myDir.XYZ() * nextStep();
5540     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5541     newNodes.push_back( newNode );
5542   }
5543
5544   if ( !myScales.empty() || !myAngles.empty() )
5545   {
5546     gp_XYZ  center = myBaseP;
5547     gp_Ax1  ratationAxis( center, myDir );
5548     gp_Trsf rotation;
5549
5550     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5551     size_t i = !makeMediumNodes;
5552     for ( beginStepIter( makeMediumNodes );
5553           moreSteps();
5554           ++nIt, i += 1 + !makeMediumNodes )
5555     {
5556       center += myDir.XYZ() * nextStep();
5557
5558       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5559       bool moved = false;
5560       if ( i < myScales.size() )
5561       {
5562         xyz = ( myScales[i] * ( xyz - center )) + center;
5563         moved = true;
5564       }
5565       if ( !myAngles.empty() )
5566       {
5567         rotation.SetRotation( ratationAxis, myAngles[i] );
5568         rotation.Transforms( xyz );
5569         moved = true;
5570       }
5571       if ( moved )
5572         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5573       else
5574         break;
5575     }
5576   }
5577   return nbNodes;
5578 }
5579
5580 //=======================================================================
5581 //function : ExtrusParam::makeNodesByDirAndSew
5582 //purpose  : create nodes for standard extrusion with sewing
5583 //=======================================================================
5584
5585 int SMESH_MeshEditor::ExtrusParam::
5586 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5587                       const SMDS_MeshNode*              srcNode,
5588                       std::list<const SMDS_MeshNode*> & newNodes,
5589                       const bool                        makeMediumNodes)
5590 {
5591   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5592
5593   int nbNodes = 0;
5594   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5595   {
5596     P1 += myDir.XYZ() * nextStep();
5597
5598     // try to search in sequence of existing nodes
5599     // if myNodes.size()>0 we 'nave to use given sequence
5600     // else - use all nodes of mesh
5601     const SMDS_MeshNode * node = 0;
5602     if ( myNodes.Length() > 0 )
5603     {
5604       for ( int i = 1; i <= myNodes.Length(); i++ )
5605       {
5606         SMESH_NodeXYZ P2 = myNodes.Value(i);
5607         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5608         {
5609           node = myNodes.Value(i);
5610           break;
5611         }
5612       }
5613     }
5614     else
5615     {
5616       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5617       while(itn->more())
5618       {
5619         SMESH_NodeXYZ P2 = itn->next();
5620         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5621         {
5622           node = P2._node;
5623           break;
5624         }
5625       }
5626     }
5627
5628     if ( !node )
5629       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5630
5631     newNodes.push_back( node );
5632
5633   } // loop on steps
5634
5635   return nbNodes;
5636 }
5637
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByNormal2D
5640 //purpose  : create nodes for extrusion using normals of faces
5641 //=======================================================================
5642
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5645                      const SMDS_MeshNode*              srcNode,
5646                      std::list<const SMDS_MeshNode*> & newNodes,
5647                      const bool                        makeMediumNodes)
5648 {
5649   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5650
5651   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5652
5653   // get normals to faces sharing srcNode
5654   vector< gp_XYZ > norms, baryCenters;
5655   gp_XYZ norm, avgNorm( 0,0,0 );
5656   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5657   while ( faceIt->more() )
5658   {
5659     const SMDS_MeshElement* face = faceIt->next();
5660     if ( myElemsToUse && !myElemsToUse->count( face ))
5661       continue;
5662     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5663     {
5664       norms.push_back( norm );
5665       avgNorm += norm;
5666       if ( !alongAvgNorm )
5667       {
5668         gp_XYZ bc(0,0,0);
5669         int nbN = 0;
5670         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5671           bc += SMESH_NodeXYZ( nIt->next() );
5672         baryCenters.push_back( bc / nbN );
5673       }
5674     }
5675   }
5676
5677   if ( norms.empty() ) return 0;
5678
5679   double normSize = avgNorm.Modulus();
5680   if ( normSize < std::numeric_limits<double>::min() )
5681     return 0;
5682
5683   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5684   {
5685     myDir = avgNorm;
5686     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5687   }
5688
5689   avgNorm /= normSize;
5690
5691   int nbNodes = 0;
5692   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5693   {
5694     gp_XYZ pNew = p;
5695     double stepSize = nextStep();
5696
5697     if ( norms.size() > 1 )
5698     {
5699       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5700       {
5701         // translate plane of a face
5702         baryCenters[ iF ] += norms[ iF ] * stepSize;
5703
5704         // find point of intersection of the face plane located at baryCenters[ iF ]
5705         // and avgNorm located at pNew
5706         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5707         double dot  = ( norms[ iF ] * avgNorm );
5708         if ( dot < std::numeric_limits<double>::min() )
5709           dot = stepSize * 1e-3;
5710         double step = -( norms[ iF ] * pNew + d ) / dot;
5711         pNew += step * avgNorm;
5712       }
5713     }
5714     else
5715     {
5716       pNew += stepSize * avgNorm;
5717     }
5718     p = pNew;
5719
5720     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5721     newNodes.push_back( newNode );
5722   }
5723   return nbNodes;
5724 }
5725
5726 //=======================================================================
5727 //function : ExtrusParam::makeNodesByNormal1D
5728 //purpose  : create nodes for extrusion using normals of edges
5729 //=======================================================================
5730
5731 int SMESH_MeshEditor::ExtrusParam::
5732 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5733                      const SMDS_MeshNode*              /*srcNode*/,
5734                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5735                      const bool                        /*makeMediumNodes*/)
5736 {
5737   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5738   return 0;
5739 }
5740
5741 //=======================================================================
5742 //function : ExtrusParam::makeNodesAlongTrack
5743 //purpose  : create nodes for extrusion along path
5744 //=======================================================================
5745
5746 int SMESH_MeshEditor::ExtrusParam::
5747 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5748                      const SMDS_MeshNode*              srcNode,
5749                      std::list<const SMDS_MeshNode*> & newNodes,
5750                      const bool                        makeMediumNodes)
5751 {
5752   const Standard_Real aTolAng=1.e-4;
5753
5754   gp_Pnt aV0x = myBaseP;
5755   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5756
5757   const PathPoint& aPP0 = myPathPoints[0];
5758   gp_Pnt aP0x = aPP0.myPnt;
5759   gp_Dir aDT0x= aPP0.myTgt;
5760
5761   std::vector< gp_Pnt > centers;
5762   centers.reserve( NbSteps() * 2 );
5763
5764   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5765
5766   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5767   {
5768     const PathPoint&  aPP  = myPathPoints[j];
5769     const gp_Pnt&     aP1x = aPP.myPnt;
5770     const gp_Dir&    aDT1x = aPP.myTgt;
5771
5772     // Translation
5773     gp_Vec aV01x( aP0x, aP1x );
5774     aTrsf.SetTranslation( aV01x );
5775     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5776     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5777
5778     // rotation 1 [ T1,T0 ]
5779     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5780     if ( fabs( aAngleT1T0 ) > aTolAng )
5781     {
5782       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5783       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5784
5785       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5786     }
5787
5788     // rotation 2
5789     if ( aPP.myAngle != 0. )
5790     {
5791       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5792       aPN1 = aPN1.Transformed( aTrsfRot );
5793     }
5794
5795     // make new node
5796     if ( makeMediumNodes )
5797     {
5798       // create additional node
5799       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5800       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5801       newNodes.push_back( newNode );
5802
5803     }
5804     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5805     newNodes.push_back( newNode );
5806
5807     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5808     centers.push_back( aV1x );
5809
5810     aPN0 = aPN1;
5811     aP0x = aP1x;
5812     aV0x = aV1x;
5813     aDT0x = aDT1x;
5814   }
5815
5816   // scale
5817   if ( !myScales.empty() )
5818   {
5819     gp_Trsf aTrsfScale;
5820     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5821     for ( size_t i = !makeMediumNodes;
5822           i < myScales.size() && node != newNodes.end();
5823           i += ( 1 + !makeMediumNodes ), ++node )
5824     {
5825       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5826       gp_Pnt aN = SMESH_NodeXYZ( *node );
5827       gp_Pnt aP = aN.Transformed( aTrsfScale );
5828       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5829     }
5830   }
5831
5832   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5833 }
5834
5835 //=======================================================================
5836 //function : ExtrusionSweep
5837 //purpose  :
5838 //=======================================================================
5839
5840 SMESH_MeshEditor::PGroupIDs
5841 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5842                                   const gp_Vec&        theStep,
5843                                   const int            theNbSteps,
5844                                   TTElemOfElemListMap& newElemsMap,
5845                                   const int            theFlags,
5846                                   const double         theTolerance)
5847 {
5848   std::list<double> dummy;
5849   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5850                        theFlags, theTolerance );
5851   return ExtrusionSweep( theElems, aParams, newElemsMap );
5852 }
5853
5854 namespace
5855 {
5856
5857 //=======================================================================
5858 //function : getOriFactor
5859 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5860 //           edge curve orientation
5861 //=======================================================================
5862
5863   double getOriFactor( const TopoDS_Edge&   edge,
5864                        const SMDS_MeshNode* n1,
5865                        const SMDS_MeshNode* n2,
5866                        SMESH_MesherHelper&  helper)
5867   {
5868     double u1 = helper.GetNodeU( edge, n1, n2 );
5869     double u2 = helper.GetNodeU( edge, n2, n1 );
5870     return u1 < u2 ? 1. : -1.;
5871   }
5872 }
5873
5874 //=======================================================================
5875 //function : ExtrusionSweep
5876 //purpose  :
5877 //=======================================================================
5878
5879 SMESH_MeshEditor::PGroupIDs
5880 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5881                                   ExtrusParam&         theParams,
5882                                   TTElemOfElemListMap& newElemsMap)
5883 {
5884   ClearLastCreated();
5885
5886   setElemsFirst( theElemSets );
5887   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5888   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5889
5890   // source elements for each generated one
5891   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892   srcElems.reserve( theElemSets[0].size() );
5893   srcNodes.reserve( theElemSets[1].size() );
5894
5895   const int nbSteps = theParams.NbSteps();
5896   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5897
5898   TNodeOfNodeListMap   mapNewNodes;
5899   TElemOfVecOfNnlmiMap mapElemNewNodes;
5900
5901   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5902                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5903                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5904   // loop on theElems
5905   TIDSortedElemSet::iterator itElem;
5906   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5907   {
5908     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5909     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5910     {
5911       // check element type
5912       const SMDS_MeshElement* elem = *itElem;
5913       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5914         continue;
5915
5916       const size_t nbNodes = elem->NbNodes();
5917       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5918       newNodesItVec.reserve( nbNodes );
5919
5920       // loop on elem nodes
5921       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5922       while ( itN->more() )
5923       {
5924         // check if a node has been already sweeped
5925         const SMDS_MeshNode* node = itN->next();
5926         TNodeOfNodeListMap::iterator nIt =
5927           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5928         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5929         if ( listNewNodes.empty() )
5930         {
5931           // make new nodes
5932
5933           // check if we are to create medium nodes between corner ones
5934           bool needMediumNodes = false;
5935           if ( isQuadraticMesh )
5936           {
5937             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5938             while (it->more() && !needMediumNodes )
5939             {
5940               const SMDS_MeshElement* invElem = it->next();
5941               if ( invElem != elem && !theElems.count( invElem )) continue;
5942               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5943               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5944                 needMediumNodes = true;
5945             }
5946           }
5947           // create nodes for all steps
5948           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5949           {
5950             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5951             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5952             {
5953               myLastCreatedNodes.push_back( *newNodesIt );
5954               srcNodes.push_back( node );
5955             }
5956           }
5957           else
5958           {
5959             if ( theParams.ToMakeBoundary() )
5960             {
5961               GetMeshDS()->Modified();
5962               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5963             }
5964             break; // newNodesItVec will be shorter than nbNodes
5965           }
5966         }
5967         newNodesItVec.push_back( nIt );
5968       }
5969       // make new elements
5970       if ( newNodesItVec.size() == nbNodes )
5971         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5972     }
5973   }
5974
5975   if ( theParams.ToMakeBoundary() ) {
5976     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5977   }
5978   PGroupIDs newGroupIDs;
5979   if ( theParams.ToMakeGroups() )
5980     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5981
5982   return newGroupIDs;
5983 }
5984
5985 //=======================================================================
5986 //function : ExtrusionAlongTrack
5987 //purpose  :
5988 //=======================================================================
5989 SMESH_MeshEditor::Extrusion_Error
5990 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5991                                        SMESH_Mesh*          theTrackMesh,
5992                                        SMDS_ElemIteratorPtr theTrackIterator,
5993                                        const SMDS_MeshNode* theN1,
5994                                        std::list<double>&   theAngles,
5995                                        const bool           theAngleVariation,
5996                                        std::list<double>&   theScales,
5997                                        const bool           theScaleVariation,
5998                                        const gp_Pnt*        theRefPoint,
5999                                        const bool           theMakeGroups)
6000 {
6001   ClearLastCreated();
6002
6003   // 1. Check data
6004   if ( theElements[0].empty() && theElements[1].empty() )
6005     return EXTR_NO_ELEMENTS;
6006
6007   ASSERT( theTrackMesh );
6008   if ( ! theTrackIterator || !theTrackIterator->more() )
6009     return EXTR_NO_ELEMENTS;
6010
6011   // 2. Get ordered nodes
6012   SMESH_MeshAlgos::TElemGroupVector branchEdges;
6013   SMESH_MeshAlgos::TNodeGroupVector branchNods;
6014   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6015   if ( branchEdges.empty() )
6016     return EXTR_PATH_NOT_EDGE;
6017
6018   if ( branchEdges.size() > 1 )
6019     return EXTR_BAD_PATH_SHAPE;
6020
6021   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
6022   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6023   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6024     return EXTR_BAD_STARTING_NODE;
6025
6026   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6027   {
6028     // add medium nodes to pathNodes
6029     std::vector< const SMDS_MeshNode* >    pathNodes2;
6030     std::vector< const SMDS_MeshElement* > pathEdges2;
6031     pathNodes2.reserve( pathNodes.size() * 2 );
6032     pathEdges2.reserve( pathEdges.size() * 2 );
6033     for ( size_t i = 0; i < pathEdges.size(); ++i )
6034     {
6035       pathNodes2.push_back( pathNodes[i] );
6036       pathEdges2.push_back( pathEdges[i] );
6037       if ( pathEdges[i]->IsQuadratic() )
6038       {
6039         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6040         pathEdges2.push_back( pathEdges[i] );
6041       }
6042     }
6043     pathNodes2.push_back( pathNodes.back() );
6044     pathEdges.swap( pathEdges2 );
6045     pathNodes.swap( pathNodes2 );
6046   }
6047
6048   // 3. Get path data at pathNodes
6049
6050   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6051
6052   if ( theAngleVariation )
6053     linearAngleVariation( points.size()-1, theAngles );
6054   if ( theScaleVariation )
6055     linearScaleVariation( points.size()-1, theScales );
6056
6057   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6058   std::list<double>::iterator angle = theAngles.begin();
6059
6060   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6061
6062   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6063   std::map< int, double >::iterator id2factor;
6064   SMESH_MesherHelper pathHelper( *theTrackMesh );
6065   gp_Pnt p; gp_Vec tangent;
6066   const double tol2 = gp::Resolution() * gp::Resolution();
6067
6068   for ( size_t i = 0; i < pathNodes.size(); ++i )
6069   {
6070     ExtrusParam::PathPoint & point = points[ i ];
6071
6072     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6073
6074     if ( angle != theAngles.end() )
6075       point.myAngle = *angle++;
6076
6077     tangent.SetCoord( 0,0,0 );
6078     const int          shapeID = pathNodes[ i ]->GetShapeID();
6079     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6080     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6081     switch ( shapeType )
6082     {
6083     case TopAbs_EDGE:
6084     {
6085       TopoDS_Edge edge = TopoDS::Edge( shape );
6086       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6087       if ( id2factor->second == 0 )
6088       {
6089         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6090         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6091       }
6092       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6093       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6094       curve->D1( u, p, tangent );
6095       tangent *= id2factor->second;
6096       break;
6097     }
6098     case TopAbs_VERTEX:
6099     {
6100       int nbEdges = 0;
6101       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6102       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6103       {
6104         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6105         for ( int di = -1; di <= 0; ++di )
6106         {
6107           size_t j = i + di;
6108           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6109           {
6110             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6111             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6112             if ( id2factor->second == 0 )
6113             {
6114               if ( j < i )
6115                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6116               else
6117                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6118             }
6119             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6120             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6121             gp_Vec du;
6122             curve->D1( u, p, du );
6123             double size2 = du.SquareMagnitude();
6124             if ( du.SquareMagnitude() > tol2 )
6125             {
6126               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6127               nbEdges++;
6128             }
6129             break;
6130           }
6131         }
6132       }
6133       if ( nbEdges > 0 )
6134         break;
6135     }
6136     // fall through
6137     default:
6138     {
6139       for ( int di = -1; di <= 1; di += 2 )
6140       {
6141         size_t j = i + di;
6142         if ( j < pathNodes.size() )
6143         {
6144           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6145           double size2 = dir.SquareMagnitude();
6146           if ( size2 > tol2 )
6147             tangent += dir.Divided( Sqrt( size2 )) * di;
6148         }
6149       }
6150     }
6151     } // switch ( shapeType )
6152
6153     if ( tangent.SquareMagnitude() < tol2 )
6154       return EXTR_CANT_GET_TANGENT;
6155
6156     point.myTgt = tangent;
6157
6158   } // loop on pathNodes
6159
6160
6161   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6162   TTElemOfElemListMap newElemsMap;
6163
6164   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6165
6166   return EXTR_OK;
6167 }
6168
6169 //=======================================================================
6170 //function : linearAngleVariation
6171 //purpose  : spread values over nbSteps
6172 //=======================================================================
6173
6174 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6175                                             list<double>& Angles)
6176 {
6177   int nbAngles = Angles.size();
6178   if( nbSteps > nbAngles && nbAngles > 0 )
6179   {
6180     vector<double> theAngles(nbAngles);
6181     theAngles.assign( Angles.begin(), Angles.end() );
6182
6183     list<double> res;
6184     double rAn2St = double( nbAngles ) / double( nbSteps );
6185     double angPrev = 0, angle;
6186     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6187     {
6188       double angCur = rAn2St * ( iSt+1 );
6189       double angCurFloor  = floor( angCur );
6190       double angPrevFloor = floor( angPrev );
6191       if ( angPrevFloor == angCurFloor )
6192         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6193       else {
6194         int iP = int( angPrevFloor );
6195         double angPrevCeil = ceil(angPrev);
6196         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6197
6198         int iC = int( angCurFloor );
6199         if ( iC < nbAngles )
6200           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6201
6202         iP = int( angPrevCeil );
6203         while ( iC-- > iP )
6204           angle += theAngles[ iC ];
6205       }
6206       res.push_back(angle);
6207       angPrev = angCur;
6208     }
6209     Angles.swap( res );
6210   }
6211 }
6212
6213 //=======================================================================
6214 //function : linearScaleVariation
6215 //purpose  : spread values over nbSteps 
6216 //=======================================================================
6217
6218 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6219                                             std::list<double>& theScales)
6220 {
6221   int nbScales = theScales.size();
6222   std::vector<double> myScales;
6223   myScales.reserve( theNbSteps );
6224   std::list<double>::const_iterator scale = theScales.begin();
6225   double prevScale = 1.0;
6226   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6227   {
6228     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6229     int    stDelta = Max( 1, iStep - myScales.size());
6230     double scDelta = ( *scale - prevScale ) / stDelta;
6231     for ( int iStep = 0; iStep < stDelta; ++iStep )
6232     {
6233       myScales.push_back( prevScale + scDelta );
6234       prevScale = myScales.back();
6235     }
6236     prevScale = *scale;
6237   }
6238   theScales.assign( myScales.begin(), myScales.end() );
6239 }
6240
6241 //================================================================================
6242 /*!
6243  * \brief Move or copy theElements applying theTrsf to their nodes
6244  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6245  *  \param theTrsf - transformation to apply
6246  *  \param theCopy - if true, create translated copies of theElems
6247  *  \param theMakeGroups - if true and theCopy, create translated groups
6248  *  \param theTargetMesh - mesh to copy translated elements into
6249  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6250  */
6251 //================================================================================
6252
6253 SMESH_MeshEditor::PGroupIDs
6254 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6255                              const gp_Trsf&     theTrsf,
6256                              const bool         theCopy,
6257                              const bool         theMakeGroups,
6258                              SMESH_Mesh*        theTargetMesh)
6259 {
6260   ClearLastCreated();
6261   myLastCreatedElems.reserve( theElems.size() );
6262
6263   bool needReverse = false;
6264   string groupPostfix;
6265   switch ( theTrsf.Form() ) {
6266   case gp_PntMirror:
6267     needReverse = true;
6268     groupPostfix = "mirrored";
6269     break;
6270   case gp_Ax1Mirror:
6271     groupPostfix = "mirrored";
6272     break;
6273   case gp_Ax2Mirror:
6274     needReverse = true;
6275     groupPostfix = "mirrored";
6276     break;
6277   case gp_Rotation:
6278     groupPostfix = "rotated";
6279     break;
6280   case gp_Translation:
6281     groupPostfix = "translated";
6282     break;
6283   case gp_Scale:
6284     groupPostfix = "scaled";
6285     break;
6286   case gp_CompoundTrsf: // different scale by axis
6287     groupPostfix = "scaled";
6288     break;
6289   default:
6290     needReverse = false;
6291     groupPostfix = "transformed";
6292   }
6293
6294   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6295   SMESHDS_Mesh* aMesh    = GetMeshDS();
6296
6297   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6298   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6299   SMESH_MeshEditor::ElemFeatures elemType;
6300
6301   // map old node to new one
6302   TNodeNodeMap nodeMap;
6303
6304   // elements sharing moved nodes; those of them which have all
6305   // nodes mirrored but are not in theElems are to be reversed
6306   TIDSortedElemSet inverseElemSet;
6307
6308   // source elements for each generated one
6309   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6310
6311   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6312   TIDSortedElemSet orphanNode;
6313
6314   if ( theElems.empty() ) // transform the whole mesh
6315   {
6316     // add all elements
6317     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6318     while ( eIt->more() ) theElems.insert( eIt->next() );
6319     // add orphan nodes
6320     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6321     while ( nIt->more() )
6322     {
6323       const SMDS_MeshNode* node = nIt->next();
6324       if ( node->NbInverseElements() == 0)
6325         orphanNode.insert( node );
6326     }
6327   }
6328
6329   // loop on elements to transform nodes : first orphan nodes then elems
6330   TIDSortedElemSet::iterator itElem;
6331   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6332   for (int i=0; i<2; i++)
6333     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6334     {
6335       const SMDS_MeshElement* elem = *itElem;
6336       if ( !elem )
6337         continue;
6338
6339       // loop on elem nodes
6340       double coord[3];
6341       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6342       while ( itN->more() )
6343       {
6344         const SMDS_MeshNode* node = cast2Node( itN->next() );
6345         // check if a node has been already transformed
6346         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6347           nodeMap.insert( make_pair ( node, node ));
6348         if ( !n2n_isnew.second )
6349           continue;
6350
6351         node->GetXYZ( coord );
6352         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6353         if ( theTargetMesh ) {
6354           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6355           n2n_isnew.first->second = newNode;
6356           myLastCreatedNodes.push_back(newNode);
6357           srcNodes.push_back( node );
6358         }
6359         else if ( theCopy ) {
6360           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6361           n2n_isnew.first->second = newNode;
6362           myLastCreatedNodes.push_back(newNode);
6363           srcNodes.push_back( node );
6364         }
6365         else {
6366           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6367           // node position on shape becomes invalid
6368           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6369             ( SMDS_SpacePosition::originSpacePosition() );
6370         }
6371
6372         // keep inverse elements
6373         if ( !theCopy && !theTargetMesh && needReverse ) {
6374           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6375           while ( invElemIt->more() ) {
6376             const SMDS_MeshElement* iel = invElemIt->next();
6377             inverseElemSet.insert( iel );
6378           }
6379         }
6380       }
6381     } // loop on elems in { &orphanNode, &theElems };
6382
6383   // either create new elements or reverse mirrored ones
6384   if ( !theCopy && !needReverse && !theTargetMesh )
6385     return PGroupIDs();
6386
6387   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6388
6389   // Replicate or reverse elements
6390
6391   std::vector<int> iForw;
6392   vector<const SMDS_MeshNode*> nodes;
6393   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6394   {
6395     const SMDS_MeshElement* elem = *itElem;
6396     if ( !elem ) continue;
6397
6398     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6399     size_t               nbNodes  = elem->NbNodes();
6400     if ( geomType == SMDSGeom_NONE ) continue; // node
6401
6402     nodes.resize( nbNodes );
6403
6404     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6405     {
6406       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6407       if ( !aPolyedre )
6408         continue;
6409       nodes.clear();
6410       bool allTransformed = true;
6411       int nbFaces = aPolyedre->NbFaces();
6412       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6413       {
6414         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6415         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6416         {
6417           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6418           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6419           if ( nodeMapIt == nodeMap.end() )
6420             allTransformed = false; // not all nodes transformed
6421           else
6422             nodes.push_back((*nodeMapIt).second);
6423         }
6424         if ( needReverse && allTransformed )
6425           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6426       }
6427       if ( !allTransformed )
6428         continue; // not all nodes transformed
6429     }
6430     else // ----------------------- the rest element types
6431     {
6432       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6433       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6434       const vector<int>&    i = needReverse ? iRev : iForw;
6435
6436       // find transformed nodes
6437       size_t iNode = 0;
6438       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6439       while ( itN->more() ) {
6440         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6441         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6442         if ( nodeMapIt == nodeMap.end() )
6443           break; // not all nodes transformed
6444         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6445       }
6446       if ( iNode != nbNodes )
6447         continue; // not all nodes transformed
6448     }
6449
6450     if ( editor ) {
6451       // copy in this or a new mesh
6452       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6453         srcElems.push_back( elem );
6454     }
6455     else {
6456       // reverse element as it was reversed by transformation
6457       if ( nbNodes > 2 )
6458         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6459     }
6460
6461   } // loop on elements
6462
6463   if ( editor && editor != this )
6464     myLastCreatedElems.swap( editor->myLastCreatedElems );
6465
6466   PGroupIDs newGroupIDs;
6467
6468   if ( ( theMakeGroups && theCopy ) ||
6469        ( theMakeGroups && theTargetMesh ) )
6470     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6471
6472   return newGroupIDs;
6473 }
6474
6475 //================================================================================
6476 /*!
6477  * \brief Make an offset mesh from a source 2D mesh
6478  *  \param [in] theElements - source faces
6479  *  \param [in] theValue - offset value
6480  *  \param [out] theTgtMesh - a mesh to add offset elements to
6481  *  \param [in] theMakeGroups - to generate groups
6482  *  \return PGroupIDs - IDs of created groups. NULL means failure
6483  */
6484 //================================================================================
6485
6486 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6487                                                       const double       theValue,
6488                                                       SMESH_Mesh*        theTgtMesh,
6489                                                       const bool         theMakeGroups,
6490                                                       const bool         theCopyElements,
6491                                                       const bool         theFixSelfIntersection)
6492 {
6493   SMESHDS_Mesh*    meshDS = GetMeshDS();
6494   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6495   SMESH_MeshEditor tgtEditor( theTgtMesh );
6496
6497   SMDS_ElemIteratorPtr eIt;
6498   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6499   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6500
6501   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6502   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6503   std::unique_ptr< SMDS_Mesh > offsetMesh
6504     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6505                                    theFixSelfIntersection,
6506                                    new2OldFaces, new2OldNodes ));
6507   if ( offsetMesh->NbElements() == 0 )
6508     return PGroupIDs(); // MakeOffset() failed
6509
6510
6511   if ( theTgtMesh == myMesh && !theCopyElements )
6512   {
6513     // clear the source elements
6514     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6515     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6516     while ( eIt->more() )
6517       meshDS->RemoveFreeElement( eIt->next(), 0 );
6518   }
6519
6520   // offsetMesh->Modified();
6521   // offsetMesh->CompactMesh(); // make IDs start from 1
6522
6523   // source elements for each generated one
6524   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6525   srcElems.reserve( new2OldFaces.size() );
6526   srcNodes.reserve( new2OldNodes.size() );
6527
6528   ClearLastCreated();
6529   myLastCreatedElems.reserve( new2OldFaces.size() );
6530   myLastCreatedNodes.reserve( new2OldNodes.size() );
6531
6532   // copy offsetMesh to theTgtMesh
6533
6534   smIdType idShift = meshDS->MaxNodeID();
6535   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6536     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6537     {
6538 #ifndef _DEBUG_
6539       if ( n->NbInverseElements() > 0 )
6540 #endif
6541       {
6542         const SMDS_MeshNode* n2 =
6543           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6544         myLastCreatedNodes.push_back( n2 );
6545         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6546       }
6547     }
6548
6549   ElemFeatures elemType;
6550   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6551     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6552     {
6553       elemType.Init( f );
6554       elemType.myNodes.clear();
6555       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6556       {
6557         const SMDS_MeshNode* n2 = nIt->next();
6558         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6559       }
6560       tgtEditor.AddElement( elemType.myNodes, elemType );
6561       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6562     }
6563
6564   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6565
6566   PGroupIDs newGroupIDs;
6567   if ( theMakeGroups )
6568     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6569   else
6570     newGroupIDs.reset( new std::list< int > );
6571
6572   return newGroupIDs;
6573 }
6574
6575 //=======================================================================
6576 /*!
6577  * \brief Create groups of elements made during transformation
6578  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6579  *  \param elemGens - elements making corresponding myLastCreatedElems
6580  *  \param postfix - to push_back to names of new groups
6581  *  \param targetMesh - mesh to create groups in
6582  *  \param topPresent - is there are "top" elements that are created by sweeping
6583  */
6584 //=======================================================================
6585
6586 SMESH_MeshEditor::PGroupIDs
6587 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6588                                  const SMESH_SequenceOfElemPtr& elemGens,
6589                                  const std::string&             postfix,
6590                                  SMESH_Mesh*                    targetMesh,
6591                                  const bool                     topPresent)
6592 {
6593   PGroupIDs newGroupIDs( new list<int> );
6594   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6595
6596   // Sort existing groups by types and collect their names
6597
6598   // containers to store an old group and generated new ones;
6599   // 1st new group is for result elems of different type than a source one;
6600   // 2nd new group is for same type result elems ("top" group at extrusion)
6601   using boost::tuple;
6602   using boost::make_tuple;
6603   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6604   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6605   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6606   // group names
6607   set< string > groupNames;
6608
6609   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6610   if ( !groupIt->more() ) return newGroupIDs;
6611
6612   int newGroupID = mesh->GetGroupIds().back()+1;
6613   while ( groupIt->more() )
6614   {
6615     SMESH_Group * group = groupIt->next();
6616     if ( !group ) continue;
6617     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6618     if ( !groupDS || groupDS->IsEmpty() ) continue;
6619     groupNames.insert    ( group->GetName() );
6620     groupDS->SetStoreName( group->GetName() );
6621     const SMDSAbs_ElementType type = groupDS->GetType();
6622     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6623     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6624     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6625     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6626   }
6627
6628   // Loop on nodes and elements to add them in new groups
6629
6630   vector< const SMDS_MeshElement* > resultElems;
6631   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6632   {
6633     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6634     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6635     if ( gens.size() != elems.size() )
6636       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6637
6638     // loop on created elements
6639     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6640     {
6641       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6642       if ( !sourceElem ) {
6643         MESSAGE("generateGroups(): NULL source element");
6644         continue;
6645       }
6646       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6647       if ( groupsOldNew.empty() ) { // no groups of this type at all
6648         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6649           ++iElem; // skip all elements made by sourceElem
6650         continue;
6651       }
6652       // collect all elements made by the iElem-th sourceElem
6653       resultElems.clear();
6654       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6655         if ( resElem != sourceElem )
6656           resultElems.push_back( resElem );
6657       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6658         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6659           if ( resElem != sourceElem )
6660             resultElems.push_back( resElem );
6661
6662       const SMDS_MeshElement* topElem = 0;
6663       if ( isNodes ) // there must be a top element
6664       {
6665         topElem = resultElems.back();
6666         resultElems.pop_back();
6667       }
6668       else
6669       {
6670         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6671         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6672           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6673           {
6674             topElem = *resElemIt;
6675             *resElemIt = 0; // erase *resElemIt
6676             break;
6677           }
6678       }
6679       // add resultElems to groups originted from ones the sourceElem belongs to
6680       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6681       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6682       {
6683         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6684         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6685         {
6686           // fill in a new group
6687           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6688           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6689           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6690             if ( *resElemIt )
6691               newGroup.Add( *resElemIt );
6692
6693           // fill a "top" group
6694           if ( topElem )
6695           {
6696             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6697             newTopGroup.Add( topElem );
6698           }
6699         }
6700       }
6701     } // loop on created elements
6702   }// loop on nodes and elements
6703
6704   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6705
6706   list<int> topGrouIds;
6707   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6708   {
6709     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6710     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6711                                       orderedOldNewGroups[i]->get<2>() };
6712     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6713     {
6714       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6715       if ( newGroupDS->IsEmpty() )
6716       {
6717         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6718       }
6719       else
6720       {
6721         // set group type
6722         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6723
6724         // make a name
6725         const bool isTop = ( topPresent &&
6726                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6727                              is2nd );
6728
6729         string name = oldGroupDS->GetStoreName();
6730         { // remove trailing whitespaces (issue 22599)
6731           size_t size = name.size();
6732           while ( size > 1 && isspace( name[ size-1 ]))
6733             --size;
6734           if ( size != name.size() )
6735           {
6736             name.resize( size );
6737             oldGroupDS->SetStoreName( name.c_str() );
6738           }
6739         }
6740         if ( !targetMesh ) {
6741           string suffix = ( isTop ? "top": postfix.c_str() );
6742           name += "_";
6743           name += suffix;
6744           int nb = 1;
6745           while ( !groupNames.insert( name ).second ) // name exists
6746             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6747         }
6748         else if ( isTop ) {
6749           name += "_top";
6750         }
6751         newGroupDS->SetStoreName( name.c_str() );
6752
6753         // make a SMESH_Groups
6754         mesh->AddGroup( newGroupDS );
6755         if ( isTop )
6756           topGrouIds.push_back( newGroupDS->GetID() );
6757         else
6758           newGroupIDs->push_back( newGroupDS->GetID() );
6759       }
6760     }
6761   }
6762   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6763
6764   return newGroupIDs;
6765 }
6766
6767 //================================================================================
6768 /*!
6769  *  * \brief Return list of group of nodes close to each other within theTolerance
6770  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6771  *  *        an Octree algorithm
6772  *  \param [in,out] theNodes - the nodes to treat
6773  *  \param [in]     theTolerance - the tolerance
6774  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6775  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6776  *         corner and medium nodes in separate groups
6777  */
6778 //================================================================================
6779
6780 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6781                                             const double         theTolerance,
6782                                             TListOfListOfNodes & theGroupsOfNodes,
6783                                             bool                 theSeparateCornersAndMedium)
6784 {
6785   ClearLastCreated();
6786
6787   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6788        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6789        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6790     theSeparateCornersAndMedium = false;
6791
6792   TIDSortedNodeSet& corners = theNodes;
6793   TIDSortedNodeSet  medium;
6794
6795   if ( theNodes.empty() ) // get all nodes in the mesh
6796   {
6797     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6798     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6799     if ( theSeparateCornersAndMedium )
6800       while ( nIt->more() )
6801       {
6802         const SMDS_MeshNode* n = nIt->next();
6803         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6804         nodeSet->insert( nodeSet->end(), n );
6805       }
6806     else
6807       while ( nIt->more() )
6808         theNodes.insert( theNodes.end(), nIt->next() );
6809   }
6810   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6811   {
6812     TIDSortedNodeSet::iterator nIt = corners.begin();
6813     while ( nIt != corners.end() )
6814       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6815       {
6816         medium.insert( medium.end(), *nIt );
6817         corners.erase( nIt++ );
6818       }
6819       else
6820       {
6821         ++nIt;
6822       }
6823   }
6824
6825   if ( !corners.empty() )
6826     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6827   if ( !medium.empty() )
6828     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6829 }
6830
6831 //=======================================================================
6832 //function : SimplifyFace
6833 //purpose  : split a chain of nodes into several closed chains
6834 //=======================================================================
6835
6836 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6837                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6838                                     vector<int>&                         quantities) const
6839 {
6840   int nbNodes = faceNodes.size();
6841   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6842     --nbNodes;
6843   if ( nbNodes < 3 )
6844     return 0;
6845   size_t prevNbQuant = quantities.size();
6846
6847   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6848   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6849   map< const SMDS_MeshNode*, int >::iterator nInd;
6850
6851   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6852   simpleNodes.push_back( faceNodes[0] );
6853   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6854   {
6855     if ( faceNodes[ iCur ] != simpleNodes.back() )
6856     {
6857       int index = simpleNodes.size();
6858       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6859       int prevIndex = nInd->second;
6860       if ( prevIndex < index )
6861       {
6862         // a sub-loop found
6863         int loopLen = index - prevIndex;
6864         if ( loopLen > 2 )
6865         {
6866           // store the sub-loop
6867           quantities.push_back( loopLen );
6868           for ( int i = prevIndex; i < index; i++ )
6869             poly_nodes.push_back( simpleNodes[ i ]);
6870         }
6871         simpleNodes.resize( prevIndex+1 );
6872       }
6873       else
6874       {
6875         simpleNodes.push_back( faceNodes[ iCur ]);
6876       }
6877     }
6878   }
6879
6880   if ( simpleNodes.size() > 2 )
6881   {
6882     quantities.push_back( simpleNodes.size() );
6883     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6884   }
6885
6886   return quantities.size() - prevNbQuant;
6887 }
6888
6889 //=======================================================================
6890 //function : MergeNodes
6891 //purpose  : In each group, the cdr of nodes are substituted by the first one
6892 //           in all elements.
6893 //=======================================================================
6894
6895 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6896                                    const bool           theAvoidMakingHoles)
6897 {
6898   ClearLastCreated();
6899
6900   SMESHDS_Mesh* mesh = GetMeshDS();
6901
6902   TNodeNodeMap nodeNodeMap; // node to replace - new node
6903   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6904   list< smIdType > rmElemIds, rmNodeIds;
6905   vector< ElemFeatures > newElemDefs;
6906
6907   // Fill nodeNodeMap and elems
6908
6909   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6910   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6911   {
6912     list<const SMDS_MeshNode*>& nodes = *grIt;
6913     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6914     const SMDS_MeshNode* nToKeep = *nIt;
6915     for ( ++nIt; nIt != nodes.end(); nIt++ )
6916     {
6917       const SMDS_MeshNode* nToRemove = *nIt;
6918       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6919       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6920       while ( invElemIt->more() ) {
6921         const SMDS_MeshElement* elem = invElemIt->next();
6922         elems.insert(elem);
6923       }
6924     }
6925   }
6926
6927   // Apply recursive replacements (BUG 0020185)
6928   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6929   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6930   {
6931     const SMDS_MeshNode* nToKeep = nnIt->second;
6932     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6933     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6934     {
6935       nToKeep = nnIt_i->second;
6936       nnIt->second = nToKeep;
6937       nnIt_i = nodeNodeMap.find( nToKeep );
6938     }
6939   }
6940
6941   if ( theAvoidMakingHoles )
6942   {
6943     // find elements whose topology changes
6944
6945     vector<const SMDS_MeshElement*> pbElems;
6946     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6947     for ( ; eIt != elems.end(); ++eIt )
6948     {
6949       const SMDS_MeshElement* elem = *eIt;
6950       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6951       while ( itN->more() )
6952       {
6953         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6954         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6955         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6956         {
6957           // several nodes of elem stick
6958           pbElems.push_back( elem );
6959           break;
6960         }
6961       }
6962     }
6963     // exclude from merge nodes causing spoiling element
6964     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6965     {
6966       bool nodesExcluded = false;
6967       for ( size_t i = 0; i < pbElems.size(); ++i )
6968       {
6969         size_t prevNbMergeNodes = nodeNodeMap.size();
6970         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6971              prevNbMergeNodes < nodeNodeMap.size() )
6972           nodesExcluded = true;
6973       }
6974       if ( !nodesExcluded )
6975         break;
6976     }
6977   }
6978
6979   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6980   {
6981     const SMDS_MeshNode* nToRemove = nnIt->first;
6982     const SMDS_MeshNode* nToKeep   = nnIt->second;
6983     if ( nToRemove != nToKeep )
6984     {
6985       rmNodeIds.push_back( nToRemove->GetID() );
6986       AddToSameGroups( nToKeep, nToRemove, mesh );
6987       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6988       // w/o creating node in place of merged ones.
6989       SMDS_PositionPtr pos = nToRemove->GetPosition();
6990       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6991         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6992           sm->SetIsAlwaysComputed( true );
6993     }
6994   }
6995
6996   // Change element nodes or remove an element
6997
6998   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6999   for ( ; eIt != elems.end(); eIt++ )
7000   {
7001     const SMDS_MeshElement* elem = *eIt;
7002     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7003
7004     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7005     if ( !keepElem )
7006       rmElemIds.push_back( elem->GetID() );
7007
7008     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7009     {
7010       bool elemChanged = false;
7011       if ( i == 0 )
7012       {
7013         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7014           elemChanged = mesh->ChangePolyhedronNodes( elem,
7015                                                      newElemDefs[i].myNodes,
7016                                                      newElemDefs[i].myPolyhedQuantities );
7017         else
7018           elemChanged = mesh->ChangeElementNodes( elem,
7019                                                   & newElemDefs[i].myNodes[0],
7020                                                   newElemDefs[i].myNodes.size() );
7021       }
7022       if ( i > 0 || !elemChanged )
7023       {
7024         if ( i == 0 )
7025         {
7026           newElemDefs[i].SetID( elem->GetID() );
7027           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7028           if ( !keepElem ) rmElemIds.pop_back();
7029         }
7030         else
7031         {
7032           newElemDefs[i].SetID( -1 );
7033         }
7034         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7035         if ( sm && newElem )
7036           sm->AddElement( newElem );
7037         if ( elem != newElem )
7038           ReplaceElemInGroups( elem, newElem, mesh );
7039       }
7040     }
7041   }
7042
7043   // Remove bad elements, then equal nodes (order important)
7044   Remove( rmElemIds, /*isNodes=*/false );
7045   Remove( rmNodeIds, /*isNodes=*/true );
7046
7047   return;
7048 }
7049
7050 //=======================================================================
7051 //function : applyMerge
7052 //purpose  : Compute new connectivity of an element after merging nodes
7053 //  \param [in] elems - the element
7054 //  \param [out] newElemDefs - definition(s) of result element(s)
7055 //  \param [inout] nodeNodeMap - nodes to merge
7056 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7057 //              after merging (but not degenerated), removes nodes causing
7058 //              the invalidity from \a nodeNodeMap.
7059 //  \return bool - true if the element should be removed
7060 //=======================================================================
7061
7062 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7063                                    vector< ElemFeatures >& newElemDefs,
7064                                    TNodeNodeMap&           nodeNodeMap,
7065                                    const bool              avoidMakingHoles )
7066 {
7067   bool toRemove = false; // to remove elem
7068   int nbResElems = 1;    // nb new elements
7069
7070   newElemDefs.resize(nbResElems);
7071   newElemDefs[0].Init( elem );
7072   newElemDefs[0].myNodes.clear();
7073
7074   set<const SMDS_MeshNode*> nodeSet;
7075   vector< const SMDS_MeshNode*>   curNodes;
7076   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7077   vector<int> iRepl;
7078
7079   const        int  nbNodes = elem->NbNodes();
7080   SMDSAbs_EntityType entity = elem->GetEntityType();
7081
7082   curNodes.resize( nbNodes );
7083   uniqueNodes.resize( nbNodes );
7084   iRepl.resize( nbNodes );
7085   int iUnique = 0, iCur = 0, nbRepl = 0;
7086
7087   // Get new seq of nodes
7088
7089   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7090   while ( itN->more() )
7091   {
7092     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7093
7094     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7095     if ( nnIt != nodeNodeMap.end() ) {
7096       n = (*nnIt).second;
7097     }
7098     curNodes[ iCur ] = n;
7099     bool isUnique = nodeSet.insert( n ).second;
7100     if ( isUnique )
7101       uniqueNodes[ iUnique++ ] = n;
7102     else
7103       iRepl[ nbRepl++ ] = iCur;
7104     iCur++;
7105   }
7106
7107   // Analyse element topology after replacement
7108
7109   int nbUniqueNodes = nodeSet.size();
7110   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7111   {
7112     toRemove = true;
7113     nbResElems = 0;
7114
7115     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7116     {
7117       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7118       int nbCorners = nbNodes / 2;
7119       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7120       {
7121         int iNext = ( iCur + 1 ) % nbCorners;
7122         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7123         {
7124           int iMedium = iCur + nbCorners;
7125           vector< const SMDS_MeshNode* >::iterator i =
7126             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7127                        uniqueNodes.end(),
7128                        curNodes[ iMedium ]);
7129           if ( i != uniqueNodes.end() )
7130           {
7131             --nbUniqueNodes;
7132             for ( ; i+1 != uniqueNodes.end(); ++i )
7133               *i = *(i+1);
7134           }
7135         }
7136       }
7137     }
7138
7139     switch ( entity )
7140     {
7141     case SMDSEntity_Polygon:
7142     case SMDSEntity_Quad_Polygon: // Polygon
7143     {
7144       ElemFeatures* elemType = & newElemDefs[0];
7145       const bool isQuad = elemType->myIsQuad;
7146       if ( isQuad )
7147         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7148           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7149
7150       // a polygon can divide into several elements
7151       vector<const SMDS_MeshNode *> polygons_nodes;
7152       vector<int> quantities;
7153       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7154       newElemDefs.resize( nbResElems );
7155       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7156       {
7157         ElemFeatures* elemType = & newElemDefs[iface];
7158         if ( iface ) elemType->Init( elem );
7159
7160         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7161         int nbNewNodes = quantities[iface];
7162         face_nodes.assign( polygons_nodes.begin() + inode,
7163                            polygons_nodes.begin() + inode + nbNewNodes );
7164         inode += nbNewNodes;
7165         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7166         {
7167           bool isValid = ( nbNewNodes % 2 == 0 );
7168           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7169             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7170           elemType->SetQuad( isValid );
7171           if ( isValid ) // put medium nodes after corners
7172             SMDS_MeshCell::applyInterlaceRev
7173               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7174                                                     nbNewNodes ), face_nodes );
7175         }
7176         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7177       }
7178       nbUniqueNodes = newElemDefs[0].myNodes.size();
7179       break;
7180     } // Polygon
7181
7182     case SMDSEntity_Polyhedra: // Polyhedral volume
7183     {
7184       if ( nbUniqueNodes >= 4 )
7185       {
7186         // each face has to be analyzed in order to check volume validity
7187         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7188         {
7189           toRemove = false;
7190           int nbFaces = aPolyedre->NbFaces();
7191
7192           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7193           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7194           vector<const SMDS_MeshNode *>  faceNodes;
7195           poly_nodes.clear();
7196           quantities.clear();
7197
7198           for (int iface = 1; iface <= nbFaces; iface++)
7199           {
7200             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7201             faceNodes.resize( nbFaceNodes );
7202             for (int inode = 1; inode <= nbFaceNodes; inode++)
7203             {
7204               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7205               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7206               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7207                 faceNode = (*nnIt).second;
7208               faceNodes[inode - 1] = faceNode;
7209             }
7210             SimplifyFace(faceNodes, poly_nodes, quantities);
7211           }
7212
7213           if ( quantities.size() > 3 )
7214           {
7215             // TODO: remove coincident faces
7216             nbResElems = 1;
7217             nbUniqueNodes = newElemDefs[0].myNodes.size();
7218           }
7219         }
7220       }
7221     }
7222     break;
7223
7224     // Regular elements
7225     // TODO not all the possible cases are solved. Find something more generic?
7226     case SMDSEntity_Edge: //////// EDGE
7227     case SMDSEntity_Triangle: //// TRIANGLE
7228     case SMDSEntity_Quad_Triangle:
7229     case SMDSEntity_Tetra:
7230     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7231     {
7232       break;
7233     }
7234     case SMDSEntity_Quad_Edge:
7235     {
7236       break;
7237     }
7238     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7239     {
7240       if ( nbUniqueNodes < 3 )
7241         toRemove = true;
7242       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7243         toRemove = true; // opposite nodes stick
7244       else
7245         toRemove = false;
7246       break;
7247     }
7248     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7249     {
7250       //   1    5    2
7251       //    +---+---+
7252       //    |       |
7253       //   4+       +6
7254       //    |       |
7255       //    +---+---+
7256       //   0    7    3
7257       if ( nbUniqueNodes == 6 &&
7258            iRepl[0] < 4       &&
7259            ( nbRepl == 1 || iRepl[1] >= 4 ))
7260       {
7261         toRemove = false;
7262       }
7263       break;
7264     }
7265     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7266     {
7267       //   1    5    2
7268       //    +---+---+
7269       //    |       |
7270       //   4+  8+   +6
7271       //    |       |
7272       //    +---+---+
7273       //   0    7    3
7274       if ( nbUniqueNodes == 7 &&
7275            iRepl[0] < 4       &&
7276            ( nbRepl == 1 || iRepl[1] != 8 ))
7277       {
7278         toRemove = false;
7279       }
7280       break;
7281     }
7282     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7283     {
7284       if ( nbUniqueNodes == 4 ) {
7285         // ---------------------------------> tetrahedron
7286         if ( curNodes[3] == curNodes[4] &&
7287              curNodes[3] == curNodes[5] ) {
7288           // top nodes stick
7289           toRemove = false;
7290         }
7291         else if ( curNodes[0] == curNodes[1] &&
7292                   curNodes[0] == curNodes[2] ) {
7293           // bottom nodes stick: set a top before
7294           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7295           uniqueNodes[ 0 ] = curNodes [ 5 ];
7296           uniqueNodes[ 1 ] = curNodes [ 4 ];
7297           uniqueNodes[ 2 ] = curNodes [ 3 ];
7298           toRemove = false;
7299         }
7300         else if (( curNodes[0] == curNodes[3] ) +
7301                  ( curNodes[1] == curNodes[4] ) +
7302                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7303           // a lateral face turns into a line
7304           toRemove = false;
7305         }
7306       }
7307       else if ( nbUniqueNodes == 5 ) {
7308         // PENTAHEDRON --------------------> pyramid
7309         if ( curNodes[0] == curNodes[3] )
7310         {
7311           uniqueNodes[ 0 ] = curNodes[ 1 ];
7312           uniqueNodes[ 1 ] = curNodes[ 4 ];
7313           uniqueNodes[ 2 ] = curNodes[ 5 ];
7314           uniqueNodes[ 3 ] = curNodes[ 2 ];
7315           uniqueNodes[ 4 ] = curNodes[ 0 ];
7316           toRemove = false;
7317         }
7318         if ( curNodes[1] == curNodes[4] )
7319         {
7320           uniqueNodes[ 0 ] = curNodes[ 0 ];
7321           uniqueNodes[ 1 ] = curNodes[ 2 ];
7322           uniqueNodes[ 2 ] = curNodes[ 5 ];
7323           uniqueNodes[ 3 ] = curNodes[ 3 ];
7324           uniqueNodes[ 4 ] = curNodes[ 1 ];
7325           toRemove = false;
7326         }
7327         if ( curNodes[2] == curNodes[5] )
7328         {
7329           uniqueNodes[ 0 ] = curNodes[ 0 ];
7330           uniqueNodes[ 1 ] = curNodes[ 3 ];
7331           uniqueNodes[ 2 ] = curNodes[ 4 ];
7332           uniqueNodes[ 3 ] = curNodes[ 1 ];
7333           uniqueNodes[ 4 ] = curNodes[ 2 ];
7334           toRemove = false;
7335         }
7336       }
7337       break;
7338     }
7339     case SMDSEntity_Hexa:
7340     {
7341       //////////////////////////////////// HEXAHEDRON
7342       SMDS_VolumeTool hexa (elem);
7343       hexa.SetExternalNormal();
7344       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7345         //////////////////////// HEX ---> tetrahedron
7346         for ( int iFace = 0; iFace < 6; iFace++ ) {
7347           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7348           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7349               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7350               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7351             // one face turns into a point ...
7352             int  pickInd = ind[ 0 ];
7353             int iOppFace = hexa.GetOppFaceIndex( iFace );
7354             ind = hexa.GetFaceNodesIndices( iOppFace );
7355             int nbStick = 0;
7356             uniqueNodes.clear();
7357             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7358               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7359                 nbStick++;
7360               else
7361                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7362             }
7363             if ( nbStick == 1 ) {
7364               // ... and the opposite one - into a triangle.
7365               // set a top node
7366               uniqueNodes.push_back( curNodes[ pickInd ]);
7367               toRemove = false;
7368             }
7369             break;
7370           }
7371         }
7372       }
7373       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7374         //////////////////////// HEX ---> prism
7375         int nbTria = 0, iTria[3];
7376         const int *ind; // indices of face nodes
7377         // look for triangular faces
7378         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7379           ind = hexa.GetFaceNodesIndices( iFace );
7380           TIDSortedNodeSet faceNodes;
7381           for ( iCur = 0; iCur < 4; iCur++ )
7382             faceNodes.insert( curNodes[ind[iCur]] );
7383           if ( faceNodes.size() == 3 )
7384             iTria[ nbTria++ ] = iFace;
7385         }
7386         // check if triangles are opposite
7387         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7388         {
7389           // set nodes of the bottom triangle
7390           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7391           vector<int> indB;
7392           for ( iCur = 0; iCur < 4; iCur++ )
7393             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7394               indB.push_back( ind[iCur] );
7395           if ( !hexa.IsForward() )
7396             std::swap( indB[0], indB[2] );
7397           for ( iCur = 0; iCur < 3; iCur++ )
7398             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7399           // set nodes of the top triangle
7400           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7401           for ( iCur = 0; iCur < 3; ++iCur )
7402             for ( int j = 0; j < 4; ++j )
7403               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7404               {
7405                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7406                 break;
7407               }
7408           toRemove = false;
7409           break;
7410         }
7411       }
7412       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7413         //////////////////// HEXAHEDRON ---> pyramid
7414         for ( int iFace = 0; iFace < 6; iFace++ ) {
7415           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7416           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7417               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7418               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7419             // one face turns into a point ...
7420             int iOppFace = hexa.GetOppFaceIndex( iFace );
7421             ind = hexa.GetFaceNodesIndices( iOppFace );
7422             uniqueNodes.clear();
7423             for ( iCur = 0; iCur < 4; iCur++ ) {
7424               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7425                 break;
7426               else
7427                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7428             }
7429             if ( uniqueNodes.size() == 4 ) {
7430               // ... and the opposite one is a quadrangle
7431               // set a top node
7432               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7433               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7434               toRemove = false;
7435             }
7436             break;
7437           }
7438         }
7439       }
7440
7441       if ( toRemove && nbUniqueNodes > 4 ) {
7442         ////////////////// HEXAHEDRON ---> polyhedron
7443         hexa.SetExternalNormal();
7444         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7445         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7446         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7447         quantities.reserve( 6 );     quantities.clear();
7448         for ( int iFace = 0; iFace < 6; iFace++ )
7449         {
7450           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7451           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7452                curNodes[ind[1]] == curNodes[ind[3]] )
7453           {
7454             quantities.clear();
7455             break; // opposite nodes stick
7456           }
7457           nodeSet.clear();
7458           for ( iCur = 0; iCur < 4; iCur++ )
7459           {
7460             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7461               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7462           }
7463           if ( nodeSet.size() < 3 )
7464             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7465           else
7466             quantities.push_back( nodeSet.size() );
7467         }
7468         if ( quantities.size() >= 4 )
7469         {
7470           nbResElems = 1;
7471           nbUniqueNodes = poly_nodes.size();
7472           newElemDefs[0].SetPoly(true);
7473         }
7474       }
7475       break;
7476     } // case HEXAHEDRON
7477
7478     default:
7479       toRemove = true;
7480
7481     } // switch ( entity )
7482
7483     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7484     {
7485       // erase from nodeNodeMap nodes whose merge spoils elem
7486       vector< const SMDS_MeshNode* > noMergeNodes;
7487       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7488       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7489         nodeNodeMap.erase( noMergeNodes[i] );
7490     }
7491     
7492   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7493
7494   uniqueNodes.resize( nbUniqueNodes );
7495
7496   if ( !toRemove && nbResElems == 0 )
7497     nbResElems = 1;
7498
7499   newElemDefs.resize( nbResElems );
7500
7501   return !toRemove;
7502 }
7503
7504
7505 // ========================================================
7506 // class   : ComparableElement
7507 // purpose : allow comparing elements basing on their nodes
7508 // ========================================================
7509
7510 class ComparableElement : public boost::container::flat_set< smIdType >
7511 {
7512   typedef boost::container::flat_set< smIdType >  int_set;
7513
7514   const SMDS_MeshElement* myElem;
7515   smIdType                mySumID;
7516   mutable int             myGroupID;
7517
7518 public:
7519
7520   ComparableElement( const SMDS_MeshElement* theElem ):
7521     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7522   {
7523     this->reserve( theElem->NbNodes() );
7524     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7525     {
7526       smIdType id = nodeIt->next()->GetID();
7527       mySumID += id;
7528       this->insert( id );
7529     }
7530   }
7531
7532   const SMDS_MeshElement* GetElem() const { return myElem; }
7533
7534   int& GroupID() const { return myGroupID; }
7535   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7536
7537   ComparableElement( const ComparableElement& theSource ) // move copy
7538     : int_set()
7539   {
7540     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7541     (int_set&) (*this ) = std::move( src );
7542     myElem    = src.myElem;
7543     mySumID   = src.mySumID;
7544     myGroupID = src.myGroupID;
7545   }
7546
7547   static int HashCode(const ComparableElement& se, int limit )
7548   {
7549     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7550   }
7551   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7552   {
7553     return ( se1 == se2 );
7554   }
7555
7556 };
7557
7558 //=======================================================================
7559 //function : FindEqualElements
7560 //purpose  : Return list of group of elements built on the same nodes.
7561 //           Search among theElements or in the whole mesh if theElements is empty
7562 //=======================================================================
7563
7564 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7565                                           TListOfListOfElementsID & theGroupsOfElementsID )
7566 {
7567   ClearLastCreated();
7568
7569   SMDS_ElemIteratorPtr elemIt;
7570   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7571   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7572
7573   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7574   typedef std::list<smIdType>                                     TGroupOfElems;
7575   TMapOfElements               mapOfElements;
7576   std::vector< TGroupOfElems > arrayOfGroups;
7577   TGroupOfElems                groupOfElems;
7578
7579   while ( elemIt->more() )
7580   {
7581     const SMDS_MeshElement* curElem = elemIt->next();
7582     if ( curElem->IsNull() )
7583       continue;
7584     ComparableElement      compElem = curElem;
7585     // check uniqueness
7586     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7587     if ( elemInSet.GetElem() != curElem ) // coincident elem
7588     {
7589       int& iG = elemInSet.GroupID();
7590       if ( iG < 0 )
7591       {
7592         iG = arrayOfGroups.size();
7593         arrayOfGroups.push_back( groupOfElems );
7594         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7595       }
7596       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7597     }
7598   }
7599
7600   groupOfElems.clear();
7601   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7602   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7603   {
7604     if ( groupIt->size() > 1 ) {
7605       //groupOfElems.sort(); -- theElements are sorted already
7606       theGroupsOfElementsID.emplace_back( *groupIt );
7607     }
7608   }
7609 }
7610
7611 //=======================================================================
7612 //function : MergeElements
7613 //purpose  : In each given group, substitute all elements by the first one.
7614 //=======================================================================
7615
7616 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7617 {
7618   ClearLastCreated();
7619
7620   typedef list<smIdType> TListOfIDs;
7621   TListOfIDs rmElemIds; // IDs of elems to remove
7622
7623   SMESHDS_Mesh* aMesh = GetMeshDS();
7624
7625   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7626   while ( groupsIt != theGroupsOfElementsID.end() ) {
7627     TListOfIDs& aGroupOfElemID = *groupsIt;
7628     aGroupOfElemID.sort();
7629     int elemIDToKeep = aGroupOfElemID.front();
7630     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7631     aGroupOfElemID.pop_front();
7632     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7633     while ( idIt != aGroupOfElemID.end() ) {
7634       int elemIDToRemove = *idIt;
7635       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7636       // add the kept element in groups of removed one (PAL15188)
7637       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7638       rmElemIds.push_back( elemIDToRemove );
7639       ++idIt;
7640     }
7641     ++groupsIt;
7642   }
7643
7644   Remove( rmElemIds, false );
7645 }
7646
7647 //=======================================================================
7648 //function : MergeEqualElements
7649 //purpose  : Remove all but one of elements built on the same nodes.
7650 //=======================================================================
7651
7652 void SMESH_MeshEditor::MergeEqualElements()
7653 {
7654   TIDSortedElemSet aMeshElements; /* empty input ==
7655                                      to merge equal elements in the whole mesh */
7656   TListOfListOfElementsID aGroupsOfElementsID;
7657   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7658   MergeElements( aGroupsOfElementsID );
7659 }
7660
7661 //=======================================================================
7662 //function : findAdjacentFace
7663 //purpose  :
7664 //=======================================================================
7665
7666 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7667                                                 const SMDS_MeshNode* n2,
7668                                                 const SMDS_MeshElement* elem)
7669 {
7670   TIDSortedElemSet elemSet, avoidSet;
7671   if ( elem )
7672     avoidSet.insert ( elem );
7673   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7674 }
7675
7676 //=======================================================================
7677 //function : findSegment
7678 //purpose  : Return a mesh segment by two nodes one of which can be medium
7679 //=======================================================================
7680
7681 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7682                                            const SMDS_MeshNode* n2)
7683 {
7684   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7685   while ( it->more() )
7686   {
7687     const SMDS_MeshElement* seg = it->next();
7688     if ( seg->GetNodeIndex( n2 ) >= 0 )
7689       return seg;
7690   }
7691   return 0;
7692 }
7693
7694 //=======================================================================
7695 //function : FindFreeBorder
7696 //purpose  :
7697 //=======================================================================
7698
7699 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7700
7701 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7702                                        const SMDS_MeshNode*             theSecondNode,
7703                                        const SMDS_MeshNode*             theLastNode,
7704                                        list< const SMDS_MeshNode* > &   theNodes,
7705                                        list< const SMDS_MeshElement* >& theFaces)
7706 {
7707   if ( !theFirstNode || !theSecondNode )
7708     return false;
7709   // find border face between theFirstNode and theSecondNode
7710   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7711   if ( !curElem )
7712     return false;
7713
7714   theFaces.push_back( curElem );
7715   theNodes.push_back( theFirstNode );
7716   theNodes.push_back( theSecondNode );
7717
7718   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7719   //TIDSortedElemSet foundElems;
7720   bool needTheLast = ( theLastNode != 0 );
7721
7722   vector<const SMDS_MeshNode*> nodes;
7723   
7724   while ( nStart != theLastNode ) {
7725     if ( nStart == theFirstNode )
7726       return !needTheLast;
7727
7728     // find all free border faces sharing nStart
7729
7730     list< const SMDS_MeshElement* > curElemList;
7731     list< const SMDS_MeshNode* >    nStartList;
7732     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7733     while ( invElemIt->more() ) {
7734       const SMDS_MeshElement* e = invElemIt->next();
7735       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7736       {
7737         // get nodes
7738         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7739                       SMDS_MeshElement::iterator() );
7740         nodes.push_back( nodes[ 0 ]);
7741
7742         // check 2 links
7743         int iNode = 0, nbNodes = nodes.size() - 1;
7744         for ( iNode = 0; iNode < nbNodes; iNode++ )
7745           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7746                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7747               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7748           {
7749             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7750             curElemList.push_back( e );
7751           }
7752       }
7753     }
7754     // analyse the found
7755
7756     int nbNewBorders = curElemList.size();
7757     if ( nbNewBorders == 0 ) {
7758       // no free border furthermore
7759       return !needTheLast;
7760     }
7761     else if ( nbNewBorders == 1 ) {
7762       // one more element found
7763       nIgnore = nStart;
7764       nStart = nStartList.front();
7765       curElem = curElemList.front();
7766       theFaces.push_back( curElem );
7767       theNodes.push_back( nStart );
7768     }
7769     else {
7770       // several continuations found
7771       list< const SMDS_MeshElement* >::iterator curElemIt;
7772       list< const SMDS_MeshNode* >::iterator nStartIt;
7773       // check if one of them reached the last node
7774       if ( needTheLast ) {
7775         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7776              curElemIt!= curElemList.end();
7777              curElemIt++, nStartIt++ )
7778           if ( *nStartIt == theLastNode ) {
7779             theFaces.push_back( *curElemIt );
7780             theNodes.push_back( *nStartIt );
7781             return true;
7782           }
7783       }
7784       // find the best free border by the continuations
7785       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7786       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7787       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7788            curElemIt!= curElemList.end();
7789            curElemIt++, nStartIt++ )
7790       {
7791         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7792         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7793         // find one more free border
7794         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7795           cNL->clear();
7796           cFL->clear();
7797         }
7798         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7799           // choice: clear a worse one
7800           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7801           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7802           contNodes[ iWorse ].clear();
7803           contFaces[ iWorse ].clear();
7804         }
7805       }
7806       if ( contNodes[0].empty() && contNodes[1].empty() )
7807         return false;
7808
7809       // push_back the best free border
7810       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7811       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7812       //theNodes.pop_back(); // remove nIgnore
7813       theNodes.pop_back(); // remove nStart
7814       //theFaces.pop_back(); // remove curElem
7815       theNodes.splice( theNodes.end(), *cNL );
7816       theFaces.splice( theFaces.end(), *cFL );
7817       return true;
7818
7819     } // several continuations found
7820   } // while ( nStart != theLastNode )
7821
7822   return true;
7823 }
7824
7825 //=======================================================================
7826 //function : CheckFreeBorderNodes
7827 //purpose  : Return true if the tree nodes are on a free border
7828 //=======================================================================
7829
7830 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7831                                             const SMDS_MeshNode* theNode2,
7832                                             const SMDS_MeshNode* theNode3)
7833 {
7834   list< const SMDS_MeshNode* > nodes;
7835   list< const SMDS_MeshElement* > faces;
7836   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7837 }
7838
7839 //=======================================================================
7840 //function : SewFreeBorder
7841 //purpose  :
7842 //warning  : for border-to-side sewing theSideSecondNode is considered as
7843 //           the last side node and theSideThirdNode is not used
7844 //=======================================================================
7845
7846 SMESH_MeshEditor::Sew_Error
7847 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7848                                  const SMDS_MeshNode* theBordSecondNode,
7849                                  const SMDS_MeshNode* theBordLastNode,
7850                                  const SMDS_MeshNode* theSideFirstNode,
7851                                  const SMDS_MeshNode* theSideSecondNode,
7852                                  const SMDS_MeshNode* theSideThirdNode,
7853                                  const bool           theSideIsFreeBorder,
7854                                  const bool           toCreatePolygons,
7855                                  const bool           toCreatePolyedrs)
7856 {
7857   ClearLastCreated();
7858
7859   Sew_Error aResult = SEW_OK;
7860
7861   // ====================================
7862   //    find side nodes and elements
7863   // ====================================
7864
7865   list< const SMDS_MeshNode* >    nSide[ 2 ];
7866   list< const SMDS_MeshElement* > eSide[ 2 ];
7867   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7868   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7869
7870   // Free border 1
7871   // --------------
7872   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7873                       nSide[0], eSide[0])) {
7874     MESSAGE(" Free Border 1 not found " );
7875     aResult = SEW_BORDER1_NOT_FOUND;
7876   }
7877   if (theSideIsFreeBorder) {
7878     // Free border 2
7879     // --------------
7880     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7881                         nSide[1], eSide[1])) {
7882       MESSAGE(" Free Border 2 not found " );
7883       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7884     }
7885   }
7886   if ( aResult != SEW_OK )
7887     return aResult;
7888
7889   if (!theSideIsFreeBorder) {
7890     // Side 2
7891     // --------------
7892
7893     // -------------------------------------------------------------------------
7894     // Algo:
7895     // 1. If nodes to merge are not coincident, move nodes of the free border
7896     //    from the coord sys defined by the direction from the first to last
7897     //    nodes of the border to the correspondent sys of the side 2
7898     // 2. On the side 2, find the links most co-directed with the correspondent
7899     //    links of the free border
7900     // -------------------------------------------------------------------------
7901
7902     // 1. Since sewing may break if there are volumes to split on the side 2,
7903     //    we won't move nodes but just compute new coordinates for them
7904     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7905     TNodeXYZMap nBordXYZ;
7906     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7907     list< const SMDS_MeshNode* >::iterator nBordIt;
7908
7909     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7910     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7911     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7912     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7913     double tol2 = 1.e-8;
7914     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7915     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7916       // Need node movement.
7917
7918       // find X and Z axes to create trsf
7919       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7920       gp_Vec X = Zs ^ Zb;
7921       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7922         // Zb || Zs
7923         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7924
7925       // coord systems
7926       gp_Ax3 toBordAx( Pb1, Zb, X );
7927       gp_Ax3 fromSideAx( Ps1, Zs, X );
7928       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7929       // set trsf
7930       gp_Trsf toBordSys, fromSide2Sys;
7931       toBordSys.SetTransformation( toBordAx );
7932       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7933       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7934
7935       // move
7936       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7937         const SMDS_MeshNode* n = *nBordIt;
7938         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7939         toBordSys.Transforms( xyz );
7940         fromSide2Sys.Transforms( xyz );
7941         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7942       }
7943     }
7944     else {
7945       // just insert nodes XYZ in the nBordXYZ map
7946       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7947         const SMDS_MeshNode* n = *nBordIt;
7948         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7949       }
7950     }
7951
7952     // 2. On the side 2, find the links most co-directed with the correspondent
7953     //    links of the free border
7954
7955     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7956     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7957     sideNodes.push_back( theSideFirstNode );
7958
7959     bool hasVolumes = false;
7960     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7961     set<long> foundSideLinkIDs, checkedLinkIDs;
7962     SMDS_VolumeTool volume;
7963     //const SMDS_MeshNode* faceNodes[ 4 ];
7964
7965     const SMDS_MeshNode*    sideNode;
7966     const SMDS_MeshElement* sideElem  = 0;
7967     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7968     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7969     nBordIt = bordNodes.begin();
7970     nBordIt++;
7971     // border node position and border link direction to compare with
7972     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7973     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7974     // choose next side node by link direction or by closeness to
7975     // the current border node:
7976     bool searchByDir = ( *nBordIt != theBordLastNode );
7977     do {
7978       // find the next node on the Side 2
7979       sideNode = 0;
7980       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7981       long linkID;
7982       checkedLinkIDs.clear();
7983       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7984
7985       // loop on inverse elements of current node (prevSideNode) on the Side 2
7986       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7987       while ( invElemIt->more() )
7988       {
7989         const SMDS_MeshElement* elem = invElemIt->next();
7990         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7991         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7992         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7993         bool isVolume = volume.Set( elem );
7994         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7995         if ( isVolume ) // --volume
7996           hasVolumes = true;
7997         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7998           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7999           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8000           while ( nIt->more() ) {
8001             nodes[ iNode ] = cast2Node( nIt->next() );
8002             if ( nodes[ iNode++ ] == prevSideNode )
8003               iPrevNode = iNode - 1;
8004           }
8005           // there are 2 links to check
8006           nbNodes = 2;
8007         }
8008         else // --edge
8009           continue;
8010         // loop on links, to be precise, on the second node of links
8011         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8012           const SMDS_MeshNode* n = nodes[ iNode ];
8013           if ( isVolume ) {
8014             if ( !volume.IsLinked( n, prevSideNode ))
8015               continue;
8016           }
8017           else {
8018             if ( iNode ) // a node before prevSideNode
8019               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8020             else         // a node after prevSideNode
8021               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8022           }
8023           // check if this link was already used
8024           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8025           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8026           if (!isJustChecked &&
8027               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8028           {
8029             // test a link geometrically
8030             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8031             bool linkIsBetter = false;
8032             double dot = 0.0, dist = 0.0;
8033             if ( searchByDir ) { // choose most co-directed link
8034               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8035               linkIsBetter = ( dot > maxDot );
8036             }
8037             else { // choose link with the node closest to bordPos
8038               dist = ( nextXYZ - bordPos ).SquareModulus();
8039               linkIsBetter = ( dist < minDist );
8040             }
8041             if ( linkIsBetter ) {
8042               maxDot = dot;
8043               minDist = dist;
8044               linkID = iLink;
8045               sideNode = n;
8046               sideElem = elem;
8047             }
8048           }
8049         }
8050       } // loop on inverse elements of prevSideNode
8051
8052       if ( !sideNode ) {
8053         MESSAGE(" Can't find path by links of the Side 2 ");
8054         return SEW_BAD_SIDE_NODES;
8055       }
8056       sideNodes.push_back( sideNode );
8057       sideElems.push_back( sideElem );
8058       foundSideLinkIDs.insert ( linkID );
8059       prevSideNode = sideNode;
8060
8061       if ( *nBordIt == theBordLastNode )
8062         searchByDir = false;
8063       else {
8064         // find the next border link to compare with
8065         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8066         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8067         // move to next border node if sideNode is before forward border node (bordPos)
8068         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8069           prevBordNode = *nBordIt;
8070           nBordIt++;
8071           bordPos = nBordXYZ[ *nBordIt ];
8072           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8073           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8074         }
8075       }
8076     }
8077     while ( sideNode != theSideSecondNode );
8078
8079     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8080       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8081       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8082     }
8083   } // end nodes search on the side 2
8084
8085   // ============================
8086   // sew the border to the side 2
8087   // ============================
8088
8089   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8090   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8091
8092   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8093   if ( toMergeConformal && toCreatePolygons )
8094   {
8095     // do not merge quadrangles if polygons are OK (IPAL0052824)
8096     eIt[0] = eSide[0].begin();
8097     eIt[1] = eSide[1].begin();
8098     bool allQuads[2] = { true, true };
8099     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8100       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8101         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8102     }
8103     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8104   }
8105
8106   TListOfListOfNodes nodeGroupsToMerge;
8107   if (( toMergeConformal ) ||
8108       ( theSideIsFreeBorder && !theSideThirdNode )) {
8109
8110     // all nodes are to be merged
8111
8112     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8113          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8114          nIt[0]++, nIt[1]++ )
8115     {
8116       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8117       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8118       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8119     }
8120   }
8121   else {
8122
8123     // insert new nodes into the border and the side to get equal nb of segments
8124
8125     // get normalized parameters of nodes on the borders
8126     vector< double > param[ 2 ];
8127     param[0].resize( maxNbNodes );
8128     param[1].resize( maxNbNodes );
8129     int iNode, iBord;
8130     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8131       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8132       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8133       const SMDS_MeshNode* nPrev = *nIt;
8134       double bordLength = 0;
8135       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8136         const SMDS_MeshNode* nCur = *nIt;
8137         gp_XYZ segment (nCur->X() - nPrev->X(),
8138                         nCur->Y() - nPrev->Y(),
8139                         nCur->Z() - nPrev->Z());
8140         double segmentLen = segment.Modulus();
8141         bordLength += segmentLen;
8142         param[ iBord ][ iNode ] = bordLength;
8143         nPrev = nCur;
8144       }
8145       // normalize within [0,1]
8146       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8147         param[ iBord ][ iNode ] /= bordLength;
8148       }
8149     }
8150
8151     // loop on border segments
8152     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8153     int i[ 2 ] = { 0, 0 };
8154     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8155     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8156
8157     // element can be split while iterating on border if it has two edges in the border
8158     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8159     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8160
8161     TElemOfNodeListMap insertMap;
8162     TElemOfNodeListMap::iterator insertMapIt;
8163     // insertMap is
8164     // key:   elem to insert nodes into
8165     // value: 2 nodes to insert between + nodes to be inserted
8166     do {
8167       bool next[ 2 ] = { false, false };
8168
8169       // find min adjacent segment length after sewing
8170       double nextParam = 10., prevParam = 0;
8171       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8172         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8173           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8174         if ( i[ iBord ] > 0 )
8175           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8176       }
8177       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8178       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8179       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8180
8181       // choose to insert or to merge nodes
8182       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8183       if ( Abs( du ) <= minSegLen * 0.2 ) {
8184         // merge
8185         // ------
8186         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8187         const SMDS_MeshNode* n0 = *nIt[0];
8188         const SMDS_MeshNode* n1 = *nIt[1];
8189         nodeGroupsToMerge.back().push_back( n1 );
8190         nodeGroupsToMerge.back().push_back( n0 );
8191         // position of node of the border changes due to merge
8192         param[ 0 ][ i[0] ] += du;
8193         // move n1 for the sake of elem shape evaluation during insertion.
8194         // n1 will be removed by MergeNodes() anyway
8195         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8196         next[0] = next[1] = true;
8197       }
8198       else {
8199         // insert
8200         // ------
8201         int intoBord = ( du < 0 ) ? 0 : 1;
8202         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8203         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8204         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8205         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8206         if ( intoBord == 1 ) {
8207           // move node of the border to be on a link of elem of the side
8208           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8209           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8210           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8211           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8212         }
8213         elemReplaceMapIt = elemReplaceMap.find( elem );
8214         if ( elemReplaceMapIt != elemReplaceMap.end() )
8215           elem = elemReplaceMapIt->second;
8216
8217         insertMapIt = insertMap.find( elem );
8218         bool  notFound = ( insertMapIt == insertMap.end() );
8219         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8220         if ( otherLink ) {
8221           // insert into another link of the same element:
8222           // 1. perform insertion into the other link of the elem
8223           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8224           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8225           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8226           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8227           // 2. perform insertion into the link of adjacent faces
8228           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8229             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8230           }
8231           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8232             InsertNodesIntoLink( seg, n12, n22, nodeList );
8233           }
8234           if (toCreatePolyedrs) {
8235             // perform insertion into the links of adjacent volumes
8236             UpdateVolumes(n12, n22, nodeList);
8237           }
8238           // 3. find an element appeared on n1 and n2 after the insertion
8239           insertMap.erase( insertMapIt );
8240           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8241           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8242           elem = elem2;
8243         }
8244         if ( notFound || otherLink ) {
8245           // add element and nodes of the side into the insertMap
8246           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8247           (*insertMapIt).second.push_back( n1 );
8248           (*insertMapIt).second.push_back( n2 );
8249         }
8250         // add node to be inserted into elem
8251         (*insertMapIt).second.push_back( nIns );
8252         next[ 1 - intoBord ] = true;
8253       }
8254
8255       // go to the next segment
8256       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8257         if ( next[ iBord ] ) {
8258           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8259             eIt[ iBord ]++;
8260           nPrev[ iBord ] = *nIt[ iBord ];
8261           nIt[ iBord ]++; i[ iBord ]++;
8262         }
8263       }
8264     }
8265     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8266
8267     // perform insertion of nodes into elements
8268
8269     for (insertMapIt = insertMap.begin();
8270          insertMapIt != insertMap.end();
8271          insertMapIt++ )
8272     {
8273       const SMDS_MeshElement* elem = (*insertMapIt).first;
8274       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8275       if ( nodeList.size() < 3 ) continue;
8276       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8277       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8278
8279       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8280
8281       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8282         InsertNodesIntoLink( seg, n1, n2, nodeList );
8283       }
8284
8285       if ( !theSideIsFreeBorder ) {
8286         // look for and insert nodes into the faces adjacent to elem
8287         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8288           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8289         }
8290       }
8291       if (toCreatePolyedrs) {
8292         // perform insertion into the links of adjacent volumes
8293         UpdateVolumes(n1, n2, nodeList);
8294       }
8295     }
8296   } // end: insert new nodes
8297
8298   MergeNodes ( nodeGroupsToMerge );
8299
8300
8301   // Remove coincident segments
8302
8303   // get new segments
8304   TIDSortedElemSet segments;
8305   SMESH_SequenceOfElemPtr newFaces;
8306   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8307   {
8308     if ( !myLastCreatedElems[i] ) continue;
8309     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8310       segments.insert( segments.end(), myLastCreatedElems[i] );
8311     else
8312       newFaces.push_back( myLastCreatedElems[i] );
8313   }
8314   // get segments adjacent to merged nodes
8315   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8316   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8317   {
8318     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8319     if ( nodes.front()->IsNull() ) continue;
8320     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8321     while ( segIt->more() )
8322       segments.insert( segIt->next() );
8323   }
8324
8325   // find coincident
8326   TListOfListOfElementsID equalGroups;
8327   if ( !segments.empty() )
8328     FindEqualElements( segments, equalGroups );
8329   if ( !equalGroups.empty() )
8330   {
8331     // remove from segments those that will be removed
8332     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8333     for ( ; itGroups != equalGroups.end(); ++itGroups )
8334     {
8335       list< smIdType >& group = *itGroups;
8336       list< smIdType >::iterator id = group.begin();
8337       for ( ++id; id != group.end(); ++id )
8338         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8339           segments.erase( seg );
8340     }
8341     // remove equal segments
8342     MergeElements( equalGroups );
8343
8344     // restore myLastCreatedElems
8345     myLastCreatedElems = newFaces;
8346     TIDSortedElemSet::iterator seg = segments.begin();
8347     for ( ; seg != segments.end(); ++seg )
8348       myLastCreatedElems.push_back( *seg );
8349   }
8350
8351   return aResult;
8352 }
8353
8354 //=======================================================================
8355 //function : InsertNodesIntoLink
8356 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8357 //           and theBetweenNode2 and split theElement
8358 //=======================================================================
8359
8360 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8361                                            const SMDS_MeshNode*        theBetweenNode1,
8362                                            const SMDS_MeshNode*        theBetweenNode2,
8363                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8364                                            const bool                  toCreatePoly)
8365 {
8366   if ( !theElement ) return;
8367
8368   SMESHDS_Mesh *aMesh = GetMeshDS();
8369   vector<const SMDS_MeshElement*> newElems;
8370
8371   if ( theElement->GetType() == SMDSAbs_Edge )
8372   {
8373     theNodesToInsert.push_front( theBetweenNode1 );
8374     theNodesToInsert.push_back ( theBetweenNode2 );
8375     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8376     const SMDS_MeshNode* n1 = *n;
8377     for ( ++n; n != theNodesToInsert.end(); ++n )
8378     {
8379       const SMDS_MeshNode* n2 = *n;
8380       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8381         AddToSameGroups( seg, theElement, aMesh );
8382       else
8383         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8384       n1 = n2;
8385     }
8386     theNodesToInsert.pop_front();
8387     theNodesToInsert.pop_back();
8388
8389     if ( theElement->IsQuadratic() ) // add a not split part
8390     {
8391       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8392                                           theElement->end_nodes() );
8393       int iOther = 0, nbN = nodes.size();
8394       for ( ; iOther < nbN; ++iOther )
8395         if ( nodes[iOther] != theBetweenNode1 &&
8396              nodes[iOther] != theBetweenNode2 )
8397           break;
8398       if      ( iOther == 0 )
8399       {
8400         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8401           AddToSameGroups( seg, theElement, aMesh );
8402         else
8403           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8404       }
8405       else if ( iOther == 2 )
8406       {
8407         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8408           AddToSameGroups( seg, theElement, aMesh );
8409         else
8410           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8411       }
8412     }
8413     // treat new elements
8414     for ( size_t i = 0; i < newElems.size(); ++i )
8415       if ( newElems[i] )
8416       {
8417         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8418         myLastCreatedElems.push_back( newElems[i] );
8419       }
8420     ReplaceElemInGroups( theElement, newElems, aMesh );
8421     aMesh->RemoveElement( theElement );
8422     return;
8423
8424   } // if ( theElement->GetType() == SMDSAbs_Edge )
8425
8426   const SMDS_MeshElement* theFace = theElement;
8427   if ( theFace->GetType() != SMDSAbs_Face ) return;
8428
8429   // find indices of 2 link nodes and of the rest nodes
8430   int iNode = 0, il1, il2, i3, i4;
8431   il1 = il2 = i3 = i4 = -1;
8432   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8433
8434   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8435   while ( nodeIt->more() ) {
8436     const SMDS_MeshNode* n = nodeIt->next();
8437     if ( n == theBetweenNode1 )
8438       il1 = iNode;
8439     else if ( n == theBetweenNode2 )
8440       il2 = iNode;
8441     else if ( i3 < 0 )
8442       i3 = iNode;
8443     else
8444       i4 = iNode;
8445     nodes[ iNode++ ] = n;
8446   }
8447   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8448     return ;
8449
8450   // arrange link nodes to go one after another regarding the face orientation
8451   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8452   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8453   if ( reverse ) {
8454     iNode = il1;
8455     il1 = il2;
8456     il2 = iNode;
8457     aNodesToInsert.reverse();
8458   }
8459   // check that not link nodes of a quadrangles are in good order
8460   int nbFaceNodes = theFace->NbNodes();
8461   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8462     iNode = i3;
8463     i3 = i4;
8464     i4 = iNode;
8465   }
8466
8467   if (toCreatePoly || theFace->IsPoly()) {
8468
8469     iNode = 0;
8470     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8471
8472     // add nodes of face up to first node of link
8473     bool isFLN = false;
8474     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8475     while ( nodeIt->more() && !isFLN ) {
8476       const SMDS_MeshNode* n = nodeIt->next();
8477       poly_nodes[iNode++] = n;
8478       isFLN = ( n == nodes[il1] );
8479     }
8480     // add nodes to insert
8481     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8482     for (; nIt != aNodesToInsert.end(); nIt++) {
8483       poly_nodes[iNode++] = *nIt;
8484     }
8485     // add nodes of face starting from last node of link
8486     while ( nodeIt->more() ) {
8487       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8488       poly_nodes[iNode++] = n;
8489     }
8490
8491     // make a new face
8492     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8493   }
8494
8495   else if ( !theFace->IsQuadratic() )
8496   {
8497     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8498     int nbLinkNodes = 2 + aNodesToInsert.size();
8499     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8500     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8501     linkNodes[ 0 ] = nodes[ il1 ];
8502     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8503     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8504     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8505       linkNodes[ iNode++ ] = *nIt;
8506     }
8507     // decide how to split a quadrangle: compare possible variants
8508     // and choose which of splits to be a quadrangle
8509     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8510     if ( nbFaceNodes == 3 ) {
8511       iBestQuad = nbSplits;
8512       i4 = i3;
8513     }
8514     else if ( nbFaceNodes == 4 ) {
8515       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8516       double aBestRate = DBL_MAX;
8517       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8518         i1 = 0; i2 = 1;
8519         double aBadRate = 0;
8520         // evaluate elements quality
8521         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8522           if ( iSplit == iQuad ) {
8523             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8524                                    linkNodes[ i2++ ],
8525                                    nodes[ i3 ],
8526                                    nodes[ i4 ]);
8527             aBadRate += getBadRate( &quad, aCrit );
8528           }
8529           else {
8530             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8531                                    linkNodes[ i2++ ],
8532                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8533             aBadRate += getBadRate( &tria, aCrit );
8534           }
8535         }
8536         // choice
8537         if ( aBadRate < aBestRate ) {
8538           iBestQuad = iQuad;
8539           aBestRate = aBadRate;
8540         }
8541       }
8542     }
8543
8544     // create new elements
8545     i1 = 0; i2 = 1;
8546     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8547     {
8548       if ( iSplit == iBestQuad )
8549         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8550                                             linkNodes[ i2++ ],
8551                                             nodes[ i3 ],
8552                                             nodes[ i4 ]));
8553       else
8554         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8555                                             linkNodes[ i2++ ],
8556                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8557     }
8558
8559     const SMDS_MeshNode* newNodes[ 4 ];
8560     newNodes[ 0 ] = linkNodes[ i1 ];
8561     newNodes[ 1 ] = linkNodes[ i2 ];
8562     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8563     newNodes[ 3 ] = nodes[ i4 ];
8564     if (iSplit == iBestQuad)
8565       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8566     else
8567       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8568
8569   } // end if(!theFace->IsQuadratic())
8570
8571   else { // theFace is quadratic
8572     // we have to split theFace on simple triangles and one simple quadrangle
8573     int tmp = il1/2;
8574     int nbshift = tmp*2;
8575     // shift nodes in nodes[] by nbshift
8576     int i,j;
8577     for(i=0; i<nbshift; i++) {
8578       const SMDS_MeshNode* n = nodes[0];
8579       for(j=0; j<nbFaceNodes-1; j++) {
8580         nodes[j] = nodes[j+1];
8581       }
8582       nodes[nbFaceNodes-1] = n;
8583     }
8584     il1 = il1 - nbshift;
8585     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8586     //   n0      n1     n2    n0      n1     n2
8587     //     +-----+-----+        +-----+-----+
8588     //      \         /         |           |
8589     //       \       /          |           |
8590     //      n5+     +n3       n7+           +n3
8591     //         \   /            |           |
8592     //          \ /             |           |
8593     //           +              +-----+-----+
8594     //           n4           n6      n5     n4
8595
8596     // create new elements
8597     int n1,n2,n3;
8598     if ( nbFaceNodes == 6 ) { // quadratic triangle
8599       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8600       if ( theFace->IsMediumNode(nodes[il1]) ) {
8601         // create quadrangle
8602         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8603         n1 = 1;
8604         n2 = 2;
8605         n3 = 3;
8606       }
8607       else {
8608         // create quadrangle
8609         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8610         n1 = 0;
8611         n2 = 1;
8612         n3 = 5;
8613       }
8614     }
8615     else { // nbFaceNodes==8 - quadratic quadrangle
8616       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8617       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8618       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8619       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8620         // create quadrangle
8621         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8622         n1 = 1;
8623         n2 = 2;
8624         n3 = 3;
8625       }
8626       else {
8627         // create quadrangle
8628         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8629         n1 = 0;
8630         n2 = 1;
8631         n3 = 7;
8632       }
8633     }
8634     // create needed triangles using n1,n2,n3 and inserted nodes
8635     int nbn = 2 + aNodesToInsert.size();
8636     vector<const SMDS_MeshNode*> aNodes(nbn);
8637     aNodes[0    ] = nodes[n1];
8638     aNodes[nbn-1] = nodes[n2];
8639     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8640     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8641       aNodes[iNode++] = *nIt;
8642     }
8643     for ( i = 1; i < nbn; i++ )
8644       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8645   }
8646
8647   // remove the old face
8648   for ( size_t i = 0; i < newElems.size(); ++i )
8649     if ( newElems[i] )
8650     {
8651       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8652       myLastCreatedElems.push_back( newElems[i] );
8653     }
8654   ReplaceElemInGroups( theFace, newElems, aMesh );
8655   aMesh->RemoveElement(theFace);
8656
8657 } // InsertNodesIntoLink()
8658
8659 //=======================================================================
8660 //function : UpdateVolumes
8661 //purpose  :
8662 //=======================================================================
8663
8664 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8665                                       const SMDS_MeshNode*        theBetweenNode2,
8666                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8667 {
8668   ClearLastCreated();
8669
8670   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8671   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8672     const SMDS_MeshElement* elem = invElemIt->next();
8673
8674     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8675     SMDS_VolumeTool aVolume (elem);
8676     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8677       continue;
8678
8679     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8680     int iface, nbFaces = aVolume.NbFaces();
8681     vector<const SMDS_MeshNode *> poly_nodes;
8682     vector<int> quantities (nbFaces);
8683
8684     for (iface = 0; iface < nbFaces; iface++) {
8685       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8686       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8687       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8688
8689       for (int inode = 0; inode < nbFaceNodes; inode++) {
8690         poly_nodes.push_back(faceNodes[inode]);
8691
8692         if (nbInserted == 0) {
8693           if (faceNodes[inode] == theBetweenNode1) {
8694             if (faceNodes[inode + 1] == theBetweenNode2) {
8695               nbInserted = theNodesToInsert.size();
8696
8697               // add nodes to insert
8698               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8699               for (; nIt != theNodesToInsert.end(); nIt++) {
8700                 poly_nodes.push_back(*nIt);
8701               }
8702             }
8703           }
8704           else if (faceNodes[inode] == theBetweenNode2) {
8705             if (faceNodes[inode + 1] == theBetweenNode1) {
8706               nbInserted = theNodesToInsert.size();
8707
8708               // add nodes to insert in reversed order
8709               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8710               nIt--;
8711               for (; nIt != theNodesToInsert.begin(); nIt--) {
8712                 poly_nodes.push_back(*nIt);
8713               }
8714               poly_nodes.push_back(*nIt);
8715             }
8716           }
8717           else {
8718           }
8719         }
8720       }
8721       quantities[iface] = nbFaceNodes + nbInserted;
8722     }
8723
8724     // Replace the volume
8725     SMESHDS_Mesh *aMesh = GetMeshDS();
8726
8727     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8728     {
8729       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8730       myLastCreatedElems.push_back( newElem );
8731       ReplaceElemInGroups( elem, newElem, aMesh );
8732     }
8733     aMesh->RemoveElement( elem );
8734   }
8735 }
8736
8737 namespace
8738 {
8739   //================================================================================
8740   /*!
8741    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8742    */
8743   //================================================================================
8744
8745   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8746                            vector<const SMDS_MeshNode *> & nodes,
8747                            vector<int> &                   nbNodeInFaces )
8748   {
8749     nodes.clear();
8750     nbNodeInFaces.clear();
8751     SMDS_VolumeTool vTool ( elem );
8752     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8753     {
8754       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8755       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8756       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8757     }
8758   }
8759 }
8760
8761 //=======================================================================
8762 /*!
8763  * \brief Convert elements contained in a sub-mesh to quadratic
8764  * \return int - nb of checked elements
8765  */
8766 //=======================================================================
8767
8768 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8769                                                   SMESH_MesherHelper& theHelper,
8770                                                   const bool          theForce3d)
8771 {
8772   //MESSAGE("convertElemToQuadratic");
8773   smIdType nbElem = 0;
8774   if( !theSm ) return nbElem;
8775
8776   vector<int> nbNodeInFaces;
8777   vector<const SMDS_MeshNode *> nodes;
8778   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8779   while(ElemItr->more())
8780   {
8781     nbElem++;
8782     const SMDS_MeshElement* elem = ElemItr->next();
8783     if( !elem ) continue;
8784
8785     // analyse a necessity of conversion
8786     const SMDSAbs_ElementType aType = elem->GetType();
8787     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8788       continue;
8789     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8790     bool hasCentralNodes = false;
8791     if ( elem->IsQuadratic() )
8792     {
8793       bool alreadyOK;
8794       switch ( aGeomType ) {
8795       case SMDSEntity_Quad_Triangle:
8796       case SMDSEntity_Quad_Quadrangle:
8797       case SMDSEntity_Quad_Hexa:
8798       case SMDSEntity_Quad_Penta:
8799         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8800
8801       case SMDSEntity_BiQuad_Triangle:
8802       case SMDSEntity_BiQuad_Quadrangle:
8803       case SMDSEntity_TriQuad_Hexa:
8804       case SMDSEntity_BiQuad_Penta:
8805         alreadyOK = theHelper.GetIsBiQuadratic();
8806         hasCentralNodes = true;
8807         break;
8808       default:
8809         alreadyOK = true;
8810       }
8811       // take into account already present medium nodes
8812       switch ( aType ) {
8813       case SMDSAbs_Volume:
8814         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8815       case SMDSAbs_Face:
8816         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8817       case SMDSAbs_Edge:
8818         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8819       default:;
8820       }
8821       if ( alreadyOK )
8822         continue;
8823     }
8824     // get elem data needed to re-create it
8825     //
8826     const smIdType id = elem->GetID();
8827     const int nbNodes = elem->NbCornerNodes();
8828     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8829     if ( aGeomType == SMDSEntity_Polyhedra )
8830       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8831     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8832       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8833
8834     // remove a linear element
8835     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8836
8837     // remove central nodes of biquadratic elements (biquad->quad conversion)
8838     if ( hasCentralNodes )
8839       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8840         if ( nodes[i]->NbInverseElements() == 0 )
8841           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8842
8843     const SMDS_MeshElement* NewElem = 0;
8844
8845     switch( aType )
8846     {
8847     case SMDSAbs_Edge :
8848     {
8849       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8850       break;
8851     }
8852     case SMDSAbs_Face :
8853     {
8854       switch(nbNodes)
8855       {
8856       case 3:
8857         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8858         break;
8859       case 4:
8860         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8861         break;
8862       default:
8863         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8864       }
8865       break;
8866     }
8867     case SMDSAbs_Volume :
8868     {
8869       switch( aGeomType )
8870       {
8871       case SMDSEntity_Tetra:
8872         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8873         break;
8874       case SMDSEntity_Pyramid:
8875         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8876         break;
8877       case SMDSEntity_Penta:
8878       case SMDSEntity_Quad_Penta:
8879       case SMDSEntity_BiQuad_Penta:
8880         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8881         break;
8882       case SMDSEntity_Hexa:
8883       case SMDSEntity_Quad_Hexa:
8884       case SMDSEntity_TriQuad_Hexa:
8885         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8886                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8887         break;
8888       case SMDSEntity_Hexagonal_Prism:
8889       default:
8890         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8891       }
8892       break;
8893     }
8894     default :
8895       continue;
8896     }
8897     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8898     if( NewElem && NewElem->getshapeId() < 1 )
8899       theSm->AddElement( NewElem );
8900   }
8901   return nbElem;
8902 }
8903 //=======================================================================
8904 //function : ConvertToQuadratic
8905 //purpose  :
8906 //=======================================================================
8907
8908 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8909 {
8910   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8911   SMESHDS_Mesh* meshDS = GetMeshDS();
8912
8913   SMESH_MesherHelper aHelper(*myMesh);
8914
8915   aHelper.SetIsQuadratic( true );
8916   aHelper.SetIsBiQuadratic( theToBiQuad );
8917   aHelper.SetElementsOnShape(true);
8918   aHelper.ToFixNodeParameters( true );
8919
8920   // convert elements assigned to sub-meshes
8921   smIdType nbCheckedElems = 0;
8922   if ( myMesh->HasShapeToMesh() )
8923   {
8924     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8925     {
8926       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8927       while ( smIt->more() ) {
8928         SMESH_subMesh* sm = smIt->next();
8929         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8930           aHelper.SetSubShape( sm->GetSubShape() );
8931           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8932         }
8933       }
8934     }
8935   }
8936
8937   // convert elements NOT assigned to sub-meshes
8938   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8939   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8940   {
8941     aHelper.SetElementsOnShape(false);
8942     SMESHDS_SubMesh *smDS = 0;
8943
8944     // convert edges
8945     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8946     while( aEdgeItr->more() )
8947     {
8948       const SMDS_MeshEdge* edge = aEdgeItr->next();
8949       if ( !edge->IsQuadratic() )
8950       {
8951         smIdType                  id = edge->GetID();
8952         const SMDS_MeshNode* n1 = edge->GetNode(0);
8953         const SMDS_MeshNode* n2 = edge->GetNode(1);
8954
8955         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8956
8957         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8958         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8959       }
8960       else
8961       {
8962         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8963       }
8964     }
8965
8966     // convert faces
8967     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8968     while( aFaceItr->more() )
8969     {
8970       const SMDS_MeshFace* face = aFaceItr->next();
8971       if ( !face ) continue;
8972       
8973       const SMDSAbs_EntityType type = face->GetEntityType();
8974       bool alreadyOK;
8975       switch( type )
8976       {
8977       case SMDSEntity_Quad_Triangle:
8978       case SMDSEntity_Quad_Quadrangle:
8979         alreadyOK = !theToBiQuad;
8980         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8981         break;
8982       case SMDSEntity_BiQuad_Triangle:
8983       case SMDSEntity_BiQuad_Quadrangle:
8984         alreadyOK = theToBiQuad;
8985         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8986         break;
8987       default: alreadyOK = false;
8988       }
8989       if ( alreadyOK )
8990         continue;
8991
8992       const smIdType id = face->GetID();
8993       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8994
8995       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8996
8997       SMDS_MeshFace * NewFace = 0;
8998       switch( type )
8999       {
9000       case SMDSEntity_Triangle:
9001       case SMDSEntity_Quad_Triangle:
9002       case SMDSEntity_BiQuad_Triangle:
9003         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9004         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9005           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9006         break;
9007
9008       case SMDSEntity_Quadrangle:
9009       case SMDSEntity_Quad_Quadrangle:
9010       case SMDSEntity_BiQuad_Quadrangle:
9011         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9012         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9013           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9014         break;
9015
9016       default:;
9017         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9018       }
9019       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9020     }
9021
9022     // convert volumes
9023     vector<int> nbNodeInFaces;
9024     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9025     while(aVolumeItr->more())
9026     {
9027       const SMDS_MeshVolume* volume = aVolumeItr->next();
9028       if ( !volume ) continue;
9029
9030       const SMDSAbs_EntityType type = volume->GetEntityType();
9031       if ( volume->IsQuadratic() )
9032       {
9033         bool alreadyOK;
9034         switch ( type )
9035         {
9036         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9037         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9038         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9039         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9040         default:                      alreadyOK = true;
9041         }
9042         if ( alreadyOK )
9043         {
9044           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9045           continue;
9046         }
9047       }
9048       const smIdType id = volume->GetID();
9049       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9050       if ( type == SMDSEntity_Polyhedra )
9051         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9052       else if ( type == SMDSEntity_Hexagonal_Prism )
9053         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9054
9055       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9056
9057       SMDS_MeshVolume * NewVolume = 0;
9058       switch ( type )
9059       {
9060       case SMDSEntity_Tetra:
9061         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9062         break;
9063       case SMDSEntity_Hexa:
9064       case SMDSEntity_Quad_Hexa:
9065       case SMDSEntity_TriQuad_Hexa:
9066         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9067                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9068         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9069           if ( nodes[i]->NbInverseElements() == 0 )
9070             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9071         break;
9072       case SMDSEntity_Pyramid:
9073         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9074                                       nodes[3], nodes[4], id, theForce3d);
9075         break;
9076       case SMDSEntity_Penta:
9077       case SMDSEntity_Quad_Penta:
9078       case SMDSEntity_BiQuad_Penta:
9079         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9080                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9081         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9082           if ( nodes[i]->NbInverseElements() == 0 )
9083             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9084         break;
9085       case SMDSEntity_Hexagonal_Prism:
9086       default:
9087         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9088       }
9089       ReplaceElemInGroups(volume, NewVolume, meshDS);
9090     }
9091   }
9092
9093   if ( !theForce3d )
9094   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9095     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9096     // aHelper.FixQuadraticElements(myError);
9097     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9098   }
9099 }
9100
9101 //================================================================================
9102 /*!
9103  * \brief Makes given elements quadratic
9104  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9105  *  \param theElements - elements to make quadratic
9106  */
9107 //================================================================================
9108
9109 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9110                                           TIDSortedElemSet& theElements,
9111                                           const bool        theToBiQuad)
9112 {
9113   if ( theElements.empty() ) return;
9114
9115   // we believe that all theElements are of the same type
9116   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9117
9118   // get all nodes shared by theElements
9119   TIDSortedNodeSet allNodes;
9120   TIDSortedElemSet::iterator eIt = theElements.begin();
9121   for ( ; eIt != theElements.end(); ++eIt )
9122     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9123
9124   // complete theElements with elements of lower dim whose all nodes are in allNodes
9125
9126   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9127   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9128   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9129   for ( ; nIt != allNodes.end(); ++nIt )
9130   {
9131     const SMDS_MeshNode* n = *nIt;
9132     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9133     while ( invIt->more() )
9134     {
9135       const SMDS_MeshElement*      e = invIt->next();
9136       const SMDSAbs_ElementType type = e->GetType();
9137       if ( e->IsQuadratic() )
9138       {
9139         quadAdjacentElems[ type ].insert( e );
9140
9141         bool alreadyOK;
9142         switch ( e->GetEntityType() ) {
9143         case SMDSEntity_Quad_Triangle:
9144         case SMDSEntity_Quad_Quadrangle:
9145         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9146         case SMDSEntity_BiQuad_Triangle:
9147         case SMDSEntity_BiQuad_Quadrangle:
9148         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9149         default:                           alreadyOK = true;
9150         }
9151         if ( alreadyOK )
9152           continue;
9153       }
9154       if ( type >= elemType )
9155         continue; // same type or more complex linear element
9156
9157       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9158         continue; // e is already checked
9159
9160       // check nodes
9161       bool allIn = true;
9162       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9163       while ( nodeIt->more() && allIn )
9164         allIn = allNodes.count( nodeIt->next() );
9165       if ( allIn )
9166         theElements.insert(e );
9167     }
9168   }
9169
9170   SMESH_MesherHelper helper(*myMesh);
9171   helper.SetIsQuadratic( true );
9172   helper.SetIsBiQuadratic( theToBiQuad );
9173
9174   // add links of quadratic adjacent elements to the helper
9175
9176   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9177     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9178           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9179     {
9180       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9181     }
9182   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9183     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9184           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9185     {
9186       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9187     }
9188   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9189     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9190           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9191     {
9192       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9193     }
9194
9195   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9196
9197   SMESHDS_Mesh*  meshDS = GetMeshDS();
9198   SMESHDS_SubMesh* smDS = 0;
9199   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9200   {
9201     const SMDS_MeshElement* elem = *eIt;
9202
9203     bool alreadyOK;
9204     int nbCentralNodes = 0;
9205     switch ( elem->GetEntityType() ) {
9206       // linear convertible
9207     case SMDSEntity_Edge:
9208     case SMDSEntity_Triangle:
9209     case SMDSEntity_Quadrangle:
9210     case SMDSEntity_Tetra:
9211     case SMDSEntity_Pyramid:
9212     case SMDSEntity_Hexa:
9213     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9214       // quadratic that can become bi-quadratic
9215     case SMDSEntity_Quad_Triangle:
9216     case SMDSEntity_Quad_Quadrangle:
9217     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9218       // bi-quadratic
9219     case SMDSEntity_BiQuad_Triangle:
9220     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9221     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9222       // the rest
9223     default:                           alreadyOK = true;
9224     }
9225     if ( alreadyOK ) continue;
9226
9227     const SMDSAbs_ElementType type = elem->GetType();
9228     const smIdType              id = elem->GetID();
9229     const int              nbNodes = elem->NbCornerNodes();
9230     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9231
9232     helper.SetSubShape( elem->getshapeId() );
9233
9234     if ( !smDS || !smDS->Contains( elem ))
9235       smDS = meshDS->MeshElements( elem->getshapeId() );
9236     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9237
9238     SMDS_MeshElement * newElem = 0;
9239     switch( nbNodes )
9240     {
9241     case 4: // cases for most frequently used element types go first (for optimization)
9242       if ( type == SMDSAbs_Volume )
9243         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9244       else
9245         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9246       break;
9247     case 8:
9248       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9249                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9250       break;
9251     case 3:
9252       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9253       break;
9254     case 2:
9255       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9256       break;
9257     case 5:
9258       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9259                                  nodes[4], id, theForce3d);
9260       break;
9261     case 6:
9262       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9263                                  nodes[4], nodes[5], id, theForce3d);
9264       break;
9265     default:;
9266     }
9267     ReplaceElemInGroups( elem, newElem, meshDS);
9268     if( newElem && smDS )
9269       smDS->AddElement( newElem );
9270
9271     // remove central nodes
9272     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9273       if ( nodes[i]->NbInverseElements() == 0 )
9274         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9275
9276   } // loop on theElements
9277
9278   if ( !theForce3d )
9279   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9280     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9281     // helper.FixQuadraticElements( myError );
9282     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9283   }
9284 }
9285
9286 //=======================================================================
9287 /*!
9288  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9289  * \return smIdType - nb of checked elements
9290  */
9291 //=======================================================================
9292
9293 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9294                                           SMDS_ElemIteratorPtr theItr,
9295                                           const int            /*theShapeID*/)
9296 {
9297   smIdType nbElem = 0;
9298   SMESHDS_Mesh* meshDS = GetMeshDS();
9299   ElemFeatures elemType;
9300   vector<const SMDS_MeshNode *> nodes;
9301
9302   while( theItr->more() )
9303   {
9304     const SMDS_MeshElement* elem = theItr->next();
9305     nbElem++;
9306     if( elem && elem->IsQuadratic())
9307     {
9308       // get elem data
9309       int nbCornerNodes = elem->NbCornerNodes();
9310       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9311
9312       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9313
9314       //remove a quadratic element
9315       if ( !theSm || !theSm->Contains( elem ))
9316         theSm = meshDS->MeshElements( elem->getshapeId() );
9317       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9318
9319       // remove medium nodes
9320       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9321         if ( nodes[i]->NbInverseElements() == 0 )
9322           meshDS->RemoveFreeNode( nodes[i], theSm );
9323
9324       // add a linear element
9325       nodes.resize( nbCornerNodes );
9326       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9327       ReplaceElemInGroups(elem, newElem, meshDS);
9328       if( theSm && newElem )
9329         theSm->AddElement( newElem );
9330     }
9331   }
9332   return nbElem;
9333 }
9334
9335 //=======================================================================
9336 //function : ConvertFromQuadratic
9337 //purpose  :
9338 //=======================================================================
9339
9340 bool SMESH_MeshEditor::ConvertFromQuadratic()
9341 {
9342   smIdType nbCheckedElems = 0;
9343   if ( myMesh->HasShapeToMesh() )
9344   {
9345     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9346     {
9347       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9348       while ( smIt->more() ) {
9349         SMESH_subMesh* sm = smIt->next();
9350         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9351           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9352       }
9353     }
9354   }
9355
9356   smIdType totalNbElems =
9357     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9358   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9359   {
9360     SMESHDS_SubMesh *aSM = 0;
9361     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9362   }
9363
9364   return true;
9365 }
9366
9367 namespace
9368 {
9369   //================================================================================
9370   /*!
9371    * \brief Return true if all medium nodes of the element are in the node set
9372    */
9373   //================================================================================
9374
9375   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9376   {
9377     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9378       if ( !nodeSet.count( elem->GetNode(i) ))
9379         return false;
9380     return true;
9381   }
9382 }
9383
9384 //================================================================================
9385 /*!
9386  * \brief Makes given elements linear
9387  */
9388 //================================================================================
9389
9390 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9391 {
9392   if ( theElements.empty() ) return;
9393
9394   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9395   set<smIdType> mediumNodeIDs;
9396   TIDSortedElemSet::iterator eIt = theElements.begin();
9397   for ( ; eIt != theElements.end(); ++eIt )
9398   {
9399     const SMDS_MeshElement* e = *eIt;
9400     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9401       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9402   }
9403
9404   // replace given elements by linear ones
9405   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9406   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9407
9408   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9409   // except those elements sharing medium nodes of quadratic element whose medium nodes
9410   // are not all in mediumNodeIDs
9411
9412   // get remaining medium nodes
9413   TIDSortedNodeSet mediumNodes;
9414   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9415   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9416     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9417       mediumNodes.insert( mediumNodes.end(), n );
9418
9419   // find more quadratic elements to convert
9420   TIDSortedElemSet moreElemsToConvert;
9421   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9422   for ( ; nIt != mediumNodes.end(); ++nIt )
9423   {
9424     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9425     while ( invIt->more() )
9426     {
9427       const SMDS_MeshElement* e = invIt->next();
9428       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9429       {
9430         // find a more complex element including e and
9431         // whose medium nodes are not in mediumNodes
9432         bool complexFound = false;
9433         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9434         {
9435           SMDS_ElemIteratorPtr invIt2 =
9436             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9437           while ( invIt2->more() )
9438           {
9439             const SMDS_MeshElement* eComplex = invIt2->next();
9440             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9441             {
9442               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9443               if ( nbCommonNodes == e->NbNodes())
9444               {
9445                 complexFound = true;
9446                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9447                 break;
9448               }
9449             }
9450           }
9451         }
9452         if ( !complexFound )
9453           moreElemsToConvert.insert( e );
9454       }
9455     }
9456   }
9457   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9458   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9459 }
9460
9461 //=======================================================================
9462 //function : SewSideElements
9463 //purpose  :
9464 //=======================================================================
9465
9466 SMESH_MeshEditor::Sew_Error
9467 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9468                                    TIDSortedElemSet&    theSide2,
9469                                    const SMDS_MeshNode* theFirstNode1,
9470                                    const SMDS_MeshNode* theFirstNode2,
9471                                    const SMDS_MeshNode* theSecondNode1,
9472                                    const SMDS_MeshNode* theSecondNode2)
9473 {
9474   ClearLastCreated();
9475
9476   if ( theSide1.size() != theSide2.size() )
9477     return SEW_DIFF_NB_OF_ELEMENTS;
9478
9479   Sew_Error aResult = SEW_OK;
9480   // Algo:
9481   // 1. Build set of faces representing each side
9482   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9483   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9484
9485   // =======================================================================
9486   // 1. Build set of faces representing each side:
9487   // =======================================================================
9488   // a. build set of nodes belonging to faces
9489   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9490   // c. create temporary faces representing side of volumes if correspondent
9491   //    face does not exist
9492
9493   SMESHDS_Mesh* aMesh = GetMeshDS();
9494   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9495   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9496   TIDSortedElemSet             faceSet1, faceSet2;
9497   set<const SMDS_MeshElement*> volSet1,  volSet2;
9498   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9499   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9500   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9501   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9502   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9503   int iSide, iFace, iNode;
9504
9505   list<const SMDS_MeshElement* > tempFaceList;
9506   for ( iSide = 0; iSide < 2; iSide++ ) {
9507     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9508     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9509     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9510     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9511     set<const SMDS_MeshElement*>::iterator vIt;
9512     TIDSortedElemSet::iterator eIt;
9513     set<const SMDS_MeshNode*>::iterator    nIt;
9514
9515     // check that given nodes belong to given elements
9516     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9517     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9518     int firstIndex = -1, secondIndex = -1;
9519     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9520       const SMDS_MeshElement* elem = *eIt;
9521       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9522       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9523       if ( firstIndex > -1 && secondIndex > -1 ) break;
9524     }
9525     if ( firstIndex < 0 || secondIndex < 0 ) {
9526       // we can simply return until temporary faces created
9527       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9528     }
9529
9530     // -----------------------------------------------------------
9531     // 1a. Collect nodes of existing faces
9532     //     and build set of face nodes in order to detect missing
9533     //     faces corresponding to sides of volumes
9534     // -----------------------------------------------------------
9535
9536     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9537
9538     // loop on the given element of a side
9539     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9540       //const SMDS_MeshElement* elem = *eIt;
9541       const SMDS_MeshElement* elem = *eIt;
9542       if ( elem->GetType() == SMDSAbs_Face ) {
9543         faceSet->insert( elem );
9544         set <const SMDS_MeshNode*> faceNodeSet;
9545         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9546         while ( nodeIt->more() ) {
9547           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9548           nodeSet->insert( n );
9549           faceNodeSet.insert( n );
9550         }
9551         setOfFaceNodeSet.insert( faceNodeSet );
9552       }
9553       else if ( elem->GetType() == SMDSAbs_Volume )
9554         volSet->insert( elem );
9555     }
9556     // ------------------------------------------------------------------------------
9557     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9558     // ------------------------------------------------------------------------------
9559
9560     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9561       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9562       while ( fIt->more() ) { // loop on faces sharing a node
9563         const SMDS_MeshElement* f = fIt->next();
9564         if ( faceSet->find( f ) == faceSet->end() ) {
9565           // check if all nodes are in nodeSet and
9566           // complete setOfFaceNodeSet if they are
9567           set <const SMDS_MeshNode*> faceNodeSet;
9568           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9569           bool allInSet = true;
9570           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9571             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9572             if ( nodeSet->find( n ) == nodeSet->end() )
9573               allInSet = false;
9574             else
9575               faceNodeSet.insert( n );
9576           }
9577           if ( allInSet ) {
9578             faceSet->insert( f );
9579             setOfFaceNodeSet.insert( faceNodeSet );
9580           }
9581         }
9582       }
9583     }
9584
9585     // -------------------------------------------------------------------------
9586     // 1c. Create temporary faces representing sides of volumes if correspondent
9587     //     face does not exist
9588     // -------------------------------------------------------------------------
9589
9590     if ( !volSet->empty() ) {
9591       //int nodeSetSize = nodeSet->size();
9592
9593       // loop on given volumes
9594       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9595         SMDS_VolumeTool vol (*vIt);
9596         // loop on volume faces: find free faces
9597         // --------------------------------------
9598         list<const SMDS_MeshElement* > freeFaceList;
9599         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9600           if ( !vol.IsFreeFace( iFace ))
9601             continue;
9602           // check if there is already a face with same nodes in a face set
9603           const SMDS_MeshElement* aFreeFace = 0;
9604           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9605           int nbNodes = vol.NbFaceNodes( iFace );
9606           set <const SMDS_MeshNode*> faceNodeSet;
9607           vol.GetFaceNodes( iFace, faceNodeSet );
9608           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9609           if ( isNewFace ) {
9610             // no such a face is given but it still can exist, check it
9611             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9612             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9613           }
9614           if ( !aFreeFace ) {
9615             // create a temporary face
9616             if ( nbNodes == 3 ) {
9617               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9618               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9619             }
9620             else if ( nbNodes == 4 ) {
9621               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9622               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9623             }
9624             else {
9625               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9626               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9627               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9628             }
9629             if ( aFreeFace )
9630               tempFaceList.push_back( aFreeFace );
9631           }
9632
9633           if ( aFreeFace )
9634             freeFaceList.push_back( aFreeFace );
9635
9636         } // loop on faces of a volume
9637
9638         // choose one of several free faces of a volume
9639         // --------------------------------------------
9640         if ( freeFaceList.size() > 1 ) {
9641           // choose a face having max nb of nodes shared by other elems of a side
9642           int maxNbNodes = -1;
9643           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9644           while ( fIt != freeFaceList.end() ) { // loop on free faces
9645             int nbSharedNodes = 0;
9646             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9647             while ( nodeIt->more() ) { // loop on free face nodes
9648               const SMDS_MeshNode* n =
9649                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9650               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9651               while ( invElemIt->more() ) {
9652                 const SMDS_MeshElement* e = invElemIt->next();
9653                 nbSharedNodes += faceSet->count( e );
9654                 nbSharedNodes += elemSet->count( e );
9655               }
9656             }
9657             if ( nbSharedNodes > maxNbNodes ) {
9658               maxNbNodes = nbSharedNodes;
9659               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9660             }
9661             else if ( nbSharedNodes == maxNbNodes ) {
9662               fIt++;
9663             }
9664             else {
9665               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9666             }
9667           }
9668           if ( freeFaceList.size() > 1 )
9669           {
9670             // could not choose one face, use another way
9671             // choose a face most close to the bary center of the opposite side
9672             gp_XYZ aBC( 0., 0., 0. );
9673             set <const SMDS_MeshNode*> addedNodes;
9674             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9675             eIt = elemSet2->begin();
9676             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9677               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9678               while ( nodeIt->more() ) { // loop on free face nodes
9679                 const SMDS_MeshNode* n =
9680                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9681                 if ( addedNodes.insert( n ).second )
9682                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9683               }
9684             }
9685             aBC /= addedNodes.size();
9686             double minDist = DBL_MAX;
9687             fIt = freeFaceList.begin();
9688             while ( fIt != freeFaceList.end() ) { // loop on free faces
9689               double dist = 0;
9690               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9691               while ( nodeIt->more() ) { // loop on free face nodes
9692                 const SMDS_MeshNode* n =
9693                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9694                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9695                 dist += ( aBC - p ).SquareModulus();
9696               }
9697               if ( dist < minDist ) {
9698                 minDist = dist;
9699                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9700               }
9701               else
9702                 fIt = freeFaceList.erase( fIt++ );
9703             }
9704           }
9705         } // choose one of several free faces of a volume
9706
9707         if ( freeFaceList.size() == 1 ) {
9708           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9709           faceSet->insert( aFreeFace );
9710           // complete a node set with nodes of a found free face
9711           //           for ( iNode = 0; iNode < ; iNode++ )
9712           //             nodeSet->insert( fNodes[ iNode ] );
9713         }
9714
9715       } // loop on volumes of a side
9716
9717       //       // complete a set of faces if new nodes in a nodeSet appeared
9718       //       // ----------------------------------------------------------
9719       //       if ( nodeSetSize != nodeSet->size() ) {
9720       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9721       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9722       //           while ( fIt->more() ) { // loop on faces sharing a node
9723       //             const SMDS_MeshElement* f = fIt->next();
9724       //             if ( faceSet->find( f ) == faceSet->end() ) {
9725       //               // check if all nodes are in nodeSet and
9726       //               // complete setOfFaceNodeSet if they are
9727       //               set <const SMDS_MeshNode*> faceNodeSet;
9728       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9729       //               bool allInSet = true;
9730       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9731       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9732       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9733       //                   allInSet = false;
9734       //                 else
9735       //                   faceNodeSet.insert( n );
9736       //               }
9737       //               if ( allInSet ) {
9738       //                 faceSet->insert( f );
9739       //                 setOfFaceNodeSet.insert( faceNodeSet );
9740       //               }
9741       //             }
9742       //           }
9743       //         }
9744       //       }
9745     } // Create temporary faces, if there are volumes given
9746   } // loop on sides
9747
9748   if ( faceSet1.size() != faceSet2.size() ) {
9749     // delete temporary faces: they are in reverseElements of actual nodes
9750     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9751     //    while ( tmpFaceIt->more() )
9752     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9753     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9754     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9755     //      aMesh->RemoveElement(*tmpFaceIt);
9756     MESSAGE("Diff nb of faces");
9757     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9758   }
9759
9760   // ============================================================
9761   // 2. Find nodes to merge:
9762   //              bind a node to remove to a node to put instead
9763   // ============================================================
9764
9765   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9766   if ( theFirstNode1 != theFirstNode2 )
9767     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9768   if ( theSecondNode1 != theSecondNode2 )
9769     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9770
9771   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9772   set< long > linkIdSet; // links to process
9773   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9774
9775   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9776   list< NLink > linkList[2];
9777   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9778   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9779   // loop on links in linkList; find faces by links and append links
9780   // of the found faces to linkList
9781   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9782   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9783   {
9784     NLink link[] = { *linkIt[0], *linkIt[1] };
9785     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9786     if ( !linkIdSet.count( linkID ) )
9787       continue;
9788
9789     // by links, find faces in the face sets,
9790     // and find indices of link nodes in the found faces;
9791     // in a face set, there is only one or no face sharing a link
9792     // ---------------------------------------------------------------
9793
9794     const SMDS_MeshElement* face[] = { 0, 0 };
9795     vector<const SMDS_MeshNode*> fnodes[2];
9796     int iLinkNode[2][2];
9797     TIDSortedElemSet avoidSet;
9798     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9799       const SMDS_MeshNode* n1 = link[iSide].first;
9800       const SMDS_MeshNode* n2 = link[iSide].second;
9801       //cout << "Side " << iSide << " ";
9802       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9803       // find a face by two link nodes
9804       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9805                                                       *faceSetPtr[ iSide ], avoidSet,
9806                                                       &iLinkNode[iSide][0],
9807                                                       &iLinkNode[iSide][1] );
9808       if ( face[ iSide ])
9809       {
9810         //cout << " F " << face[ iSide]->GetID() <<endl;
9811         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9812         // put face nodes to fnodes
9813         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9814         fnodes[ iSide ].assign( nIt, nEnd );
9815         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9816       }
9817     }
9818
9819     // check similarity of elements of the sides
9820     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9821       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9822       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9823         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9824       }
9825       else {
9826         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9827       }
9828       break; // do not return because it's necessary to remove tmp faces
9829     }
9830
9831     // set nodes to merge
9832     // -------------------
9833
9834     if ( face[0] && face[1] )  {
9835       const int nbNodes = face[0]->NbNodes();
9836       if ( nbNodes != face[1]->NbNodes() ) {
9837         MESSAGE("Diff nb of face nodes");
9838         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9839         break; // do not return because it s necessary to remove tmp faces
9840       }
9841       bool reverse[] = { false, false }; // order of nodes in the link
9842       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9843         // analyse link orientation in faces
9844         int i1 = iLinkNode[ iSide ][ 0 ];
9845         int i2 = iLinkNode[ iSide ][ 1 ];
9846         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9847       }
9848       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9849       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9850       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9851       {
9852         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9853                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9854       }
9855
9856       // add other links of the faces to linkList
9857       // -----------------------------------------
9858
9859       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9860         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9861         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9862         if ( !iter_isnew.second ) { // already in a set: no need to process
9863           linkIdSet.erase( iter_isnew.first );
9864         }
9865         else // new in set == encountered for the first time: add
9866         {
9867           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9868           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9869           linkList[0].push_back ( NLink( n1, n2 ));
9870           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9871         }
9872       }
9873     } // 2 faces found
9874
9875     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9876       break;
9877
9878   } // loop on link lists
9879
9880   if ( aResult == SEW_OK &&
9881        ( //linkIt[0] != linkList[0].end() ||
9882         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9883     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9884              " " << (faceSetPtr[1]->empty()));
9885     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9886   }
9887
9888   // ====================================================================
9889   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9890   // ====================================================================
9891
9892   // delete temporary faces
9893   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9894   //  while ( tmpFaceIt->more() )
9895   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9896   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9897   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9898     aMesh->RemoveElement(*tmpFaceIt);
9899
9900   if ( aResult != SEW_OK)
9901     return aResult;
9902
9903   list< smIdType > nodeIDsToRemove;
9904   vector< const SMDS_MeshNode*> nodes;
9905   ElemFeatures elemType;
9906
9907   // loop on nodes replacement map
9908   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9909   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9910     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9911     {
9912       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9913       nodeIDsToRemove.push_back( nToRemove->GetID() );
9914       // loop on elements sharing nToRemove
9915       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9916       while ( invElemIt->more() ) {
9917         const SMDS_MeshElement* e = invElemIt->next();
9918         // get a new suite of nodes: make replacement
9919         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9920         nodes.resize( nbNodes );
9921         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9922         while ( nIt->more() ) {
9923           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9924           nnIt = nReplaceMap.find( n );
9925           if ( nnIt != nReplaceMap.end() ) {
9926             nbReplaced++;
9927             n = (*nnIt).second;
9928           }
9929           nodes[ i++ ] = n;
9930         }
9931         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9932         //         elemIDsToRemove.push_back( e->GetID() );
9933         //       else
9934         if ( nbReplaced )
9935         {
9936           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9937           aMesh->RemoveElement( e );
9938
9939           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9940           {
9941             AddToSameGroups( newElem, e, aMesh );
9942             if ( int aShapeId = e->getshapeId() )
9943               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9944           }
9945         }
9946       }
9947     }
9948
9949   Remove( nodeIDsToRemove, true );
9950
9951   return aResult;
9952 }
9953
9954 //================================================================================
9955 /*!
9956  * \brief Find corresponding nodes in two sets of faces
9957  * \param theSide1 - first face set
9958  * \param theSide2 - second first face
9959  * \param theFirstNode1 - a boundary node of set 1
9960  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9961  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9962  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9963  * \param nReplaceMap - output map of corresponding nodes
9964  * \return bool  - is a success or not
9965  */
9966 //================================================================================
9967
9968 #ifdef _DEBUG_
9969 //#define DEBUG_MATCHING_NODES
9970 #endif
9971
9972 SMESH_MeshEditor::Sew_Error
9973 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9974                                     set<const SMDS_MeshElement*>& theSide2,
9975                                     const SMDS_MeshNode*          theFirstNode1,
9976                                     const SMDS_MeshNode*          theFirstNode2,
9977                                     const SMDS_MeshNode*          theSecondNode1,
9978                                     const SMDS_MeshNode*          theSecondNode2,
9979                                     TNodeNodeMap &                nReplaceMap)
9980 {
9981   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9982
9983   nReplaceMap.clear();
9984   //if ( theFirstNode1 != theFirstNode2 )
9985   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9986   //if ( theSecondNode1 != theSecondNode2 )
9987   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9988
9989   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9990   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9991
9992   list< NLink > linkList[2];
9993   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9994   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9995
9996   // loop on links in linkList; find faces by links and append links
9997   // of the found faces to linkList
9998   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9999   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10000     NLink link[] = { *linkIt[0], *linkIt[1] };
10001     if ( linkSet.find( link[0] ) == linkSet.end() )
10002       continue;
10003
10004     // by links, find faces in the face sets,
10005     // and find indices of link nodes in the found faces;
10006     // in a face set, there is only one or no face sharing a link
10007     // ---------------------------------------------------------------
10008
10009     const SMDS_MeshElement* face[] = { 0, 0 };
10010     list<const SMDS_MeshNode*> notLinkNodes[2];
10011     //bool reverse[] = { false, false }; // order of notLinkNodes
10012     int nbNodes[2];
10013     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10014     {
10015       const SMDS_MeshNode* n1 = link[iSide].first;
10016       const SMDS_MeshNode* n2 = link[iSide].second;
10017       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10018       set< const SMDS_MeshElement* > facesOfNode1;
10019       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10020       {
10021         // during a loop of the first node, we find all faces around n1,
10022         // during a loop of the second node, we find one face sharing both n1 and n2
10023         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10024         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10025         while ( fIt->more() ) { // loop on faces sharing a node
10026           const SMDS_MeshElement* f = fIt->next();
10027           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10028               ! facesOfNode1.insert( f ).second ) // f encounters twice
10029           {
10030             if ( face[ iSide ] ) {
10031               MESSAGE( "2 faces per link " );
10032               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10033             }
10034             face[ iSide ] = f;
10035             faceSet->erase( f );
10036
10037             // get not link nodes
10038             int nbN = f->NbNodes();
10039             if ( f->IsQuadratic() )
10040               nbN /= 2;
10041             nbNodes[ iSide ] = nbN;
10042             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10043             int i1 = f->GetNodeIndex( n1 );
10044             int i2 = f->GetNodeIndex( n2 );
10045             int iEnd = nbN, iBeg = -1, iDelta = 1;
10046             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10047             if ( reverse ) {
10048               std::swap( iEnd, iBeg ); iDelta = -1;
10049             }
10050             int i = i2;
10051             while ( true ) {
10052               i += iDelta;
10053               if ( i == iEnd ) i = iBeg + iDelta;
10054               if ( i == i1 ) break;
10055               nodes.push_back ( f->GetNode( i ) );
10056             }
10057           }
10058         }
10059       }
10060     }
10061     // check similarity of elements of the sides
10062     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10063       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10064       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10065         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10066       }
10067       else {
10068         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10069       }
10070     }
10071
10072     // set nodes to merge
10073     // -------------------
10074
10075     if ( face[0] && face[1] )  {
10076       if ( nbNodes[0] != nbNodes[1] ) {
10077         MESSAGE("Diff nb of face nodes");
10078         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10079       }
10080 #ifdef DEBUG_MATCHING_NODES
10081       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10082                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10083                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10084 #endif
10085       int nbN = nbNodes[0];
10086       {
10087         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10088         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10089         for ( int i = 0 ; i < nbN - 2; ++i ) {
10090 #ifdef DEBUG_MATCHING_NODES
10091           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10092 #endif
10093           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10094         }
10095       }
10096
10097       // add other links of the face 1 to linkList
10098       // -----------------------------------------
10099
10100       const SMDS_MeshElement* f0 = face[0];
10101       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10102       for ( int i = 0; i < nbN; i++ )
10103       {
10104         const SMDS_MeshNode* n2 = f0->GetNode( i );
10105         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10106           linkSet.insert( SMESH_TLink( n1, n2 ));
10107         if ( !iter_isnew.second ) { // already in a set: no need to process
10108           linkSet.erase( iter_isnew.first );
10109         }
10110         else // new in set == encountered for the first time: add
10111         {
10112 #ifdef DEBUG_MATCHING_NODES
10113           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10114                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10115 #endif
10116           linkList[0].push_back ( NLink( n1, n2 ));
10117           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10118         }
10119         n1 = n2;
10120       }
10121     } // 2 faces found
10122   } // loop on link lists
10123
10124   return SEW_OK;
10125 }
10126
10127 namespace // automatically find theAffectedElems for DoubleNodes()
10128 {
10129   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10130
10131   //--------------------------------------------------------------------------------
10132   // Nodes shared by adjacent FissureBorder's.
10133   // 1 node  if FissureBorder separates faces
10134   // 2 nodes if FissureBorder separates volumes
10135   struct SubBorder
10136   {
10137     const SMDS_MeshNode* _nodes[2];
10138     int                  _nbNodes;
10139
10140     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10141     {
10142       _nodes[0] = n1;
10143       _nodes[1] = n2;
10144       _nbNodes = bool( n1 ) + bool( n2 );
10145       if ( _nbNodes == 2 && n1 > n2 )
10146         std::swap( _nodes[0], _nodes[1] );
10147     }
10148     bool operator<( const SubBorder& other ) const
10149     {
10150       for ( int i = 0; i < _nbNodes; ++i )
10151       {
10152         if ( _nodes[i] < other._nodes[i] ) return true;
10153         if ( _nodes[i] > other._nodes[i] ) return false;
10154       }
10155       return false;
10156     }
10157   };
10158
10159   //--------------------------------------------------------------------------------
10160   // Map a SubBorder to all FissureBorder it bounds
10161   struct FissureBorder;
10162   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10163   typedef TBorderLinks::iterator                               TMappedSub;
10164
10165   //--------------------------------------------------------------------------------
10166   /*!
10167    * \brief Element border (volume facet or face edge) at a fissure
10168    */
10169   struct FissureBorder
10170   {
10171     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10172     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10173
10174     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10175     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10176
10177     FissureBorder( FissureBorder && from ) // move constructor
10178     {
10179       std::swap( _nodes,       from._nodes );
10180       std::swap( _sortedNodes, from._sortedNodes );
10181       _elems[0] = from._elems[0];
10182       _elems[1] = from._elems[1];
10183     }
10184
10185     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10186                    std::vector< const SMDS_MeshElement* > & adjElems)
10187       : _nodes( elemToDuplicate->NbCornerNodes() )
10188     {
10189       for ( size_t i = 0; i < _nodes.size(); ++i )
10190         _nodes[i] = elemToDuplicate->GetNode( i );
10191
10192       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10193       findAdjacent( type, adjElems );
10194     }
10195
10196     FissureBorder( const SMDS_MeshNode**                    nodes,
10197                    const size_t                             nbNodes,
10198                    const SMDSAbs_ElementType                adjElemsType,
10199                    std::vector< const SMDS_MeshElement* > & adjElems)
10200       : _nodes( nodes, nodes + nbNodes )
10201     {
10202       findAdjacent( adjElemsType, adjElems );
10203     }
10204
10205     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10206                        std::vector< const SMDS_MeshElement* > & adjElems)
10207     {
10208       _elems[0] = _elems[1] = 0;
10209       adjElems.clear();
10210       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10211         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10212           _elems[i] = adjElems[i];
10213     }
10214
10215     bool operator<( const FissureBorder& other ) const
10216     {
10217       return GetSortedNodes() < other.GetSortedNodes();
10218     }
10219
10220     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10221     {
10222       if ( _sortedNodes.empty() && !_nodes.empty() )
10223       {
10224         FissureBorder* me = const_cast<FissureBorder*>( this );
10225         me->_sortedNodes = me->_nodes;
10226         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10227       }
10228       return _sortedNodes;
10229     }
10230
10231     size_t NbSub() const
10232     {
10233       return _nodes.size();
10234     }
10235
10236     SubBorder Sub(size_t i) const
10237     {
10238       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10239     }
10240
10241     void AddSelfTo( TBorderLinks& borderLinks )
10242     {
10243       _mappedSubs.resize( NbSub() );
10244       for ( size_t i = 0; i < NbSub(); ++i )
10245       {
10246         TBorderLinks::iterator s2b =
10247           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10248         s2b->second.push_back( this );
10249         _mappedSubs[ i ] = s2b;
10250       }
10251     }
10252
10253     void Clear()
10254     {
10255       _nodes.clear();
10256     }
10257
10258     const SMDS_MeshElement* GetMarkedElem() const
10259     {
10260       if ( _nodes.empty() ) return 0; // cleared
10261       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10262       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10263       return 0;
10264     }
10265
10266     gp_XYZ GetNorm() const // normal to the border
10267     {
10268       gp_XYZ norm;
10269       if ( _nodes.size() == 2 )
10270       {
10271         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10272         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10273           avgNorm += norm;
10274         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10275           avgNorm += norm;
10276
10277         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10278         norm = bordDir ^ avgNorm;
10279       }
10280       else
10281       {
10282         SMESH_NodeXYZ p0( _nodes[0] );
10283         SMESH_NodeXYZ p1( _nodes[1] );
10284         SMESH_NodeXYZ p2( _nodes[2] );
10285         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10286       }
10287       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10288         norm.Reverse();
10289
10290       return norm;
10291     }
10292
10293     void ChooseSide() // mark an _elem located at positive side of fissure
10294     {
10295       _elems[0]->setIsMarked( true );
10296       gp_XYZ norm = GetNorm();
10297       double maxX = norm.Coord(1);
10298       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10299       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10300       if ( maxX < 0 )
10301       {
10302         _elems[0]->setIsMarked( false );
10303         if ( _elems[1] )
10304           _elems[1]->setIsMarked( true );
10305       }
10306     }
10307
10308   }; // struct FissureBorder
10309
10310   //--------------------------------------------------------------------------------
10311   /*!
10312    * \brief Classifier of elements at fissure edge
10313    */
10314   class FissureNormal
10315   {
10316     std::vector< gp_XYZ > _normals;
10317     bool                  _bothIn;
10318
10319   public:
10320     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10321     {
10322       _bothIn = false;
10323       _normals.reserve(2);
10324       _normals.push_back( bord.GetNorm() );
10325       if ( _normals.size() == 2 )
10326         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10327     }
10328
10329     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10330     {
10331       bool isIn = false;
10332       switch ( _normals.size() ) {
10333       case 1:
10334       {
10335         isIn = !isOut( n, _normals[0], elem );
10336         break;
10337       }
10338       case 2:
10339       {
10340         bool in1 = !isOut( n, _normals[0], elem );
10341         bool in2 = !isOut( n, _normals[1], elem );
10342         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10343       }
10344       }
10345       return isIn;
10346     }
10347   };
10348
10349   //================================================================================
10350   /*!
10351    * \brief Classify an element by a plane passing through a node
10352    */
10353   //================================================================================
10354
10355   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10356   {
10357     SMESH_NodeXYZ p = n;
10358     double sumDot = 0;
10359     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10360     {
10361       SMESH_NodeXYZ pi = elem->GetNode( i );
10362       sumDot += norm * ( pi - p );
10363     }
10364     return sumDot < -1e-100;
10365   }
10366
10367   //================================================================================
10368   /*!
10369    * \brief Find FissureBorder's by nodes to duplicate
10370    */
10371   //================================================================================
10372
10373   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10374                            std::vector< FissureBorder > & theFissureBorders )
10375   {
10376     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10377     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10378     if ( !n ) return;
10379     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10380     if ( n->NbInverseElements( elemType ) == 0 )
10381     {
10382       elemType = SMDSAbs_Face;
10383       if ( n->NbInverseElements( elemType ) == 0 )
10384         return;
10385     }
10386     // unmark elements touching the fissure
10387     for ( ; nIt != theNodes.end(); ++nIt )
10388       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10389
10390     // loop on elements touching the fissure to get their borders belonging to the fissure
10391     std::set< FissureBorder >              fissureBorders;
10392     std::vector< const SMDS_MeshElement* > adjElems;
10393     std::vector< const SMDS_MeshNode* >    nodes;
10394     SMDS_VolumeTool volTool;
10395     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10396     {
10397       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10398       while ( invIt->more() )
10399       {
10400         const SMDS_MeshElement* eInv = invIt->next();
10401         if ( eInv->isMarked() ) continue;
10402         eInv->setIsMarked( true );
10403
10404         if ( elemType == SMDSAbs_Volume )
10405         {
10406           volTool.Set( eInv );
10407           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10408           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10409           {
10410             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10411             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10412             nodes.clear();
10413             bool allOnFissure = true;
10414             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10415               if (( allOnFissure = theNodes.count( nn[ iN ])))
10416                 nodes.push_back( nn[ iN ]);
10417             if ( allOnFissure )
10418               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10419                                                                elemType, adjElems )));
10420           }
10421         }
10422         else // elemType == SMDSAbs_Face
10423         {
10424           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10425           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10426           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10427           {
10428             nn[1]      = eInv->GetNode( iN );
10429             onFissure1 = theNodes.count( nn[1] );
10430             if ( onFissure0 && onFissure1 )
10431               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10432             nn[0]      = nn[1];
10433             onFissure0 = onFissure1;
10434           }
10435         }
10436       }
10437     }
10438
10439     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10440     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10441     for ( ; bord != fissureBorders.end(); ++bord )
10442     {
10443       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10444     }
10445     return;
10446   } // findFissureBorders()
10447
10448   //================================================================================
10449   /*!
10450    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10451    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10452    *  \param [in] theNodesNot - nodes not to duplicate
10453    *  \param [out] theAffectedElems - the found elements
10454    */
10455   //================================================================================
10456
10457   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10458                           TIDSortedElemSet&       theAffectedElems)
10459   {
10460     if ( theElemsOrNodes.empty() ) return;
10461
10462     // find FissureBorder's
10463
10464     std::vector< FissureBorder >           fissure;
10465     std::vector< const SMDS_MeshElement* > elemsByFacet;
10466
10467     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10468     if ( (*elIt)->GetType() == SMDSAbs_Node )
10469     {
10470       findFissureBorders( theElemsOrNodes, fissure );
10471     }
10472     else
10473     {
10474       fissure.reserve( theElemsOrNodes.size() );
10475       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10476       {
10477         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10478         if ( !fissure.back()._elems[1] )
10479           fissure.pop_back();
10480       }
10481     }
10482     if ( fissure.empty() )
10483       return;
10484
10485     // fill borderLinks
10486
10487     TBorderLinks borderLinks;
10488
10489     for ( size_t i = 0; i < fissure.size(); ++i )
10490     {
10491       fissure[i].AddSelfTo( borderLinks );
10492     }
10493
10494     // get theAffectedElems
10495
10496     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10497     for ( size_t i = 0; i < fissure.size(); ++i )
10498       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10499       {
10500         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10501                                         false, /*markElem=*/true );
10502       }
10503
10504     std::vector<const SMDS_MeshNode *>                 facetNodes;
10505     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10506     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10507
10508     // choose a side of fissure
10509     fissure[0].ChooseSide();
10510     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10511
10512     size_t nbCheckedBorders = 0;
10513     while ( nbCheckedBorders < fissure.size() )
10514     {
10515       // find a FissureBorder to treat
10516       FissureBorder* bord = 0;
10517       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10518         if ( fissure[i].GetMarkedElem() )
10519           bord = & fissure[i];
10520       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10521         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10522         {
10523           bord = & fissure[i];
10524           bord->ChooseSide();
10525           theAffectedElems.insert( bord->GetMarkedElem() );
10526         }
10527       if ( !bord ) return;
10528       ++nbCheckedBorders;
10529
10530       // treat FissureBorder's linked to bord
10531       fissureNodes.clear();
10532       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10533       for ( size_t i = 0; i < bord->NbSub(); ++i )
10534       {
10535         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10536         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10537         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10538         const SubBorder&                          sb = l2b->first;
10539         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10540
10541         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10542         {
10543           for ( int j = 0; j < sb._nbNodes; ++j )
10544             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10545           continue;
10546         }
10547
10548         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10549         // until an elem adjacent to a neighbour FissureBorder is found
10550         facetNodes.clear();
10551         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10552         facetNodes.resize( sb._nbNodes + 1 );
10553
10554         while ( bordElem )
10555         {
10556           // check if bordElem is adjacent to a neighbour FissureBorder
10557           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10558           {
10559             FissureBorder* bord2 = linkedBorders[j];
10560             if ( bord2 == bord ) continue;
10561             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10562               bordElem = 0;
10563             else
10564               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10565           }
10566           if ( !bordElem )
10567             break;
10568
10569           // find the next bordElem
10570           const SMDS_MeshElement* nextBordElem = 0;
10571           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10572           {
10573             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10574             if ( fissureNodes.count( n )) continue;
10575
10576             facetNodes[ sb._nbNodes ] = n;
10577             elemsByFacet.clear();
10578             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10579             {
10580               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10581                 if ( elemsByFacet[ iE ] != bordElem &&
10582                      !elemsByFacet[ iE ]->isMarked() )
10583                 {
10584                   theAffectedElems.insert( elemsByFacet[ iE ]);
10585                   elemsByFacet[ iE ]->setIsMarked( true );
10586                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10587                     nextBordElem = elemsByFacet[ iE ];
10588                 }
10589             }
10590           }
10591           bordElem = nextBordElem;
10592
10593         } // while ( bordElem )
10594
10595         linkedBorders.clear(); // not to treat this link any more
10596
10597       } // loop on SubBorder's of a FissureBorder
10598
10599       bord->Clear();
10600
10601     } // loop on FissureBorder's
10602
10603
10604     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10605
10606     // mark nodes of theAffectedElems
10607     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10608
10609     // unmark nodes of the fissure
10610     elIt = theElemsOrNodes.begin();
10611     if ( (*elIt)->GetType() == SMDSAbs_Node )
10612       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10613     else
10614       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10615
10616     std::vector< gp_XYZ > normVec;
10617
10618     // loop on nodes of the fissure, add elements having marked nodes
10619     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10620     {
10621       const SMDS_MeshElement* e = (*elIt);
10622       if ( e->GetType() != SMDSAbs_Node )
10623         e->setIsMarked( true ); // avoid adding a fissure element
10624
10625       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10626       {
10627         const SMDS_MeshNode* n = e->GetNode( iN );
10628         if ( fissEdgeNodes2Norm.count( n ))
10629           continue;
10630
10631         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10632         while ( invIt->more() )
10633         {
10634           const SMDS_MeshElement* eInv = invIt->next();
10635           if ( eInv->isMarked() ) continue;
10636           eInv->setIsMarked( true );
10637
10638           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10639           while( nIt->more() )
10640             if ( nIt->next()->isMarked())
10641             {
10642               theAffectedElems.insert( eInv );
10643               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10644               n->setIsMarked( false );
10645               break;
10646             }
10647         }
10648       }
10649     }
10650
10651     // add elements on the fissure edge
10652     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10653     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10654     {
10655       const SMDS_MeshNode* edgeNode = n2N->first;
10656       const FissureNormal & normals = n2N->second;
10657
10658       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10659       while ( invIt->more() )
10660       {
10661         const SMDS_MeshElement* eInv = invIt->next();
10662         if ( eInv->isMarked() ) continue;
10663         eInv->setIsMarked( true );
10664
10665         // classify eInv using normals
10666         bool toAdd = normals.IsIn( edgeNode, eInv );
10667         if ( toAdd ) // check if all nodes lie on the fissure edge
10668         {
10669           bool notOnEdge = false;
10670           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10671             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10672           toAdd = notOnEdge;
10673         }
10674         if ( toAdd )
10675         {
10676           theAffectedElems.insert( eInv );
10677         }
10678       }
10679     }
10680
10681     return;
10682   } // findAffectedElems()
10683 } // namespace
10684
10685 //================================================================================
10686 /*!
10687  * \brief Create elements equal (on same nodes) to given ones
10688  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10689  *              elements of the uppest dimension are duplicated.
10690  */
10691 //================================================================================
10692
10693 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10694 {
10695   ClearLastCreated();
10696   SMESHDS_Mesh* mesh = GetMeshDS();
10697
10698   // get an element type and an iterator over elements
10699
10700   SMDSAbs_ElementType type = SMDSAbs_All;
10701   SMDS_ElemIteratorPtr elemIt;
10702   if ( theElements.empty() )
10703   {
10704     if ( mesh->NbNodes() == 0 )
10705       return;
10706     // get most complex type
10707     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10708       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10709       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10710     };
10711     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10712       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10713       {
10714         type = types[i];
10715         elemIt = mesh->elementsIterator( type );
10716         break;
10717       }
10718   }
10719   else
10720   {
10721     //type = (*theElements.begin())->GetType();
10722     elemIt = SMESHUtils::elemSetIterator( theElements );
10723   }
10724
10725   // un-mark all elements to avoid duplicating just created elements
10726   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10727
10728   // duplicate elements
10729
10730   ElemFeatures elemType;
10731
10732   vector< const SMDS_MeshNode* > nodes;
10733   while ( elemIt->more() )
10734   {
10735     const SMDS_MeshElement* elem = elemIt->next();
10736     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10737         ( elem->isMarked() ))
10738       continue;
10739
10740     elemType.Init( elem, /*basicOnly=*/false );
10741     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10742
10743     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10744       newElem->setIsMarked( true );
10745   }
10746 }
10747
10748 //================================================================================
10749 /*!
10750   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10751   \param theElems - the list of elements (edges or faces) to be replicated
10752   The nodes for duplication could be found from these elements
10753   \param theNodesNot - list of nodes to NOT replicate
10754   \param theAffectedElems - the list of elements (cells and edges) to which the
10755   replicated nodes should be associated to.
10756   \return TRUE if operation has been completed successfully, FALSE otherwise
10757 */
10758 //================================================================================
10759
10760 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10761                                     const TIDSortedElemSet& theNodesNot,
10762                                     const TIDSortedElemSet& theAffectedElems )
10763 {
10764   ClearLastCreated();
10765
10766   if ( theElems.size() == 0 )
10767     return false;
10768
10769   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10770   if ( !aMeshDS )
10771     return false;
10772
10773   bool res = false;
10774   TNodeNodeMap anOldNodeToNewNode;
10775   // duplicate elements and nodes
10776   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10777   // replce nodes by duplications
10778   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10779   return res;
10780 }
10781
10782 //================================================================================
10783 /*!
10784   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10785   \param theMeshDS - mesh instance
10786   \param theElems - the elements replicated or modified (nodes should be changed)
10787   \param theNodesNot - nodes to NOT replicate
10788   \param theNodeNodeMap - relation of old node to new created node
10789   \param theIsDoubleElem - flag os to replicate element or modify
10790   \return TRUE if operation has been completed successfully, FALSE otherwise
10791 */
10792 //================================================================================
10793
10794 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10795                                    const TIDSortedElemSet& theElems,
10796                                    const TIDSortedElemSet& theNodesNot,
10797                                    TNodeNodeMap&           theNodeNodeMap,
10798                                    const bool              theIsDoubleElem )
10799 {
10800   // iterate through element and duplicate them (by nodes duplication)
10801   bool res = false;
10802   std::vector<const SMDS_MeshNode*> newNodes;
10803   ElemFeatures elemType;
10804
10805   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10806   for ( ;  elemItr != theElems.end(); ++elemItr )
10807   {
10808     const SMDS_MeshElement* anElem = *elemItr;
10809     // if (!anElem)
10810     //   continue;
10811
10812     // duplicate nodes to duplicate element
10813     bool isDuplicate = false;
10814     newNodes.resize( anElem->NbNodes() );
10815     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10816     int ind = 0;
10817     while ( anIter->more() )
10818     {
10819       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10820       const SMDS_MeshNode*  aNewNode = aCurrNode;
10821       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10822       if ( n2n != theNodeNodeMap.end() )
10823       {
10824         aNewNode = n2n->second;
10825       }
10826       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10827       {
10828         // duplicate node
10829         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10830         copyPosition( aCurrNode, aNewNode );
10831         theNodeNodeMap[ aCurrNode ] = aNewNode;
10832         myLastCreatedNodes.push_back( aNewNode );
10833       }
10834       isDuplicate |= (aCurrNode != aNewNode);
10835       newNodes[ ind++ ] = aNewNode;
10836     }
10837     if ( !isDuplicate )
10838       continue;
10839
10840     if ( theIsDoubleElem )
10841       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10842     else
10843     {
10844       // change element nodes
10845       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
10846       if ( geomType == SMDSEntity_Polyhedra )
10847       {
10848         // special treatment for polyhedron
10849         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
10850         if (!aPolyhedron) {
10851           MESSAGE("Warning: bad volumic element");
10852           return false;
10853         }
10854         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
10855       }
10856       else
10857         // standard entity type
10858         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10859     }
10860
10861     res = true;
10862   }
10863   return res;
10864 }
10865
10866 //================================================================================
10867 /*!
10868   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10869   \param theNodes - identifiers of nodes to be doubled
10870   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10871   nodes. If list of element identifiers is empty then nodes are doubled but
10872   they not assigned to elements
10873   \return TRUE if operation has been completed successfully, FALSE otherwise
10874 */
10875 //================================================================================
10876
10877 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10878                                     const std::list< int >& theListOfModifiedElems )
10879 {
10880   ClearLastCreated();
10881
10882   if ( theListOfNodes.size() == 0 )
10883     return false;
10884
10885   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10886   if ( !aMeshDS )
10887     return false;
10888
10889   // iterate through nodes and duplicate them
10890
10891   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10892
10893   std::list< int >::const_iterator aNodeIter;
10894   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10895   {
10896     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10897     if ( !aNode )
10898       continue;
10899
10900     // duplicate node
10901
10902     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10903     if ( aNewNode )
10904     {
10905       copyPosition( aNode, aNewNode );
10906       anOldNodeToNewNode[ aNode ] = aNewNode;
10907       myLastCreatedNodes.push_back( aNewNode );
10908     }
10909   }
10910
10911   // Change nodes of elements
10912
10913   std::vector<const SMDS_MeshNode*> aNodeArr;
10914
10915   std::list< int >::const_iterator anElemIter;
10916   for ( anElemIter =  theListOfModifiedElems.begin();
10917         anElemIter != theListOfModifiedElems.end();
10918         anElemIter++ )
10919   {
10920     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10921     if ( !anElem )
10922       continue;
10923
10924     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10925     for( size_t i = 0; i < aNodeArr.size(); ++i )
10926     {
10927       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10928         anOldNodeToNewNode.find( aNodeArr[ i ]);
10929       if ( n2n != anOldNodeToNewNode.end() )
10930         aNodeArr[ i ] = n2n->second;
10931     }
10932     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10933   }
10934
10935   return true;
10936 }
10937
10938 namespace {
10939
10940   //================================================================================
10941   /*!
10942     \brief Check if element located inside shape
10943     \return TRUE if IN or ON shape, FALSE otherwise
10944   */
10945   //================================================================================
10946
10947   template<class Classifier>
10948   bool isInside(const SMDS_MeshElement* theElem,
10949                 Classifier&             theClassifier,
10950                 const double            theTol)
10951   {
10952     gp_XYZ centerXYZ (0, 0, 0);
10953     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10954       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10955
10956     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10957     theClassifier.Perform(aPnt, theTol);
10958     TopAbs_State aState = theClassifier.State();
10959     return (aState == TopAbs_IN || aState == TopAbs_ON );
10960   }
10961
10962   //================================================================================
10963   /*!
10964    * \brief Classifier of the 3D point on the TopoDS_Face
10965    *        with interaface suitable for isInside()
10966    */
10967   //================================================================================
10968
10969   struct _FaceClassifier
10970   {
10971     Extrema_ExtPS       _extremum;
10972     BRepAdaptor_Surface _surface;
10973     TopAbs_State        _state;
10974
10975     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10976     {
10977       _extremum.Initialize( _surface,
10978                             _surface.FirstUParameter(), _surface.LastUParameter(),
10979                             _surface.FirstVParameter(), _surface.LastVParameter(),
10980                             _surface.Tolerance(), _surface.Tolerance() );
10981     }
10982     void Perform(const gp_Pnt& aPnt, double theTol)
10983     {
10984       theTol *= theTol;
10985       _state = TopAbs_OUT;
10986       _extremum.Perform(aPnt);
10987       if ( _extremum.IsDone() )
10988         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10989           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10990     }
10991     TopAbs_State State() const
10992     {
10993       return _state;
10994     }
10995   };
10996 }
10997
10998 //================================================================================
10999 /*!
11000   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11001   This method is the first step of DoubleNodeElemGroupsInRegion.
11002   \param theElems - list of groups of elements (edges or faces) to be replicated
11003   \param theNodesNot - list of groups of nodes not to replicate
11004   \param theShape - shape to detect affected elements (element which geometric center
11005          located on or inside shape). If the shape is null, detection is done on faces orientations
11006          (select elements with a gravity center on the side given by faces normals).
11007          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11008          The replicated nodes should be associated to affected elements.
11009   \return true
11010   \sa DoubleNodeElemGroupsInRegion()
11011 */
11012 //================================================================================
11013
11014 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11015                                                    const TIDSortedElemSet& theNodesNot,
11016                                                    const TopoDS_Shape&     theShape,
11017                                                    TIDSortedElemSet&       theAffectedElems)
11018 {
11019   if ( theShape.IsNull() )
11020   {
11021     findAffectedElems( theElems, theAffectedElems );
11022   }
11023   else
11024   {
11025     const double aTol = Precision::Confusion();
11026     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11027     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11028     if ( theShape.ShapeType() == TopAbs_SOLID )
11029     {
11030       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11031       bsc3d->PerformInfinitePoint(aTol);
11032     }
11033     else if (theShape.ShapeType() == TopAbs_FACE )
11034     {
11035       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11036     }
11037
11038     // iterates on indicated elements and get elements by back references from their nodes
11039     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11040     for ( ;  elemItr != theElems.end(); ++elemItr )
11041     {
11042       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11043       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11044       while ( nodeItr->more() )
11045       {
11046         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11047         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11048           continue;
11049         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11050         while ( backElemItr->more() )
11051         {
11052           const SMDS_MeshElement* curElem = backElemItr->next();
11053           if ( curElem && theElems.find(curElem) == theElems.end() &&
11054                ( bsc3d.get() ?
11055                  isInside( curElem, *bsc3d, aTol ) :
11056                  isInside( curElem, *aFaceClassifier, aTol )))
11057             theAffectedElems.insert( curElem );
11058         }
11059       }
11060     }
11061   }
11062   return true;
11063 }
11064
11065 //================================================================================
11066 /*!
11067   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11068   \param theElems - group of of elements (edges or faces) to be replicated
11069   \param theNodesNot - group of nodes not to replicate
11070   \param theShape - shape to detect affected elements (element which geometric center
11071   located on or inside shape).
11072   The replicated nodes should be associated to affected elements.
11073   \return TRUE if operation has been completed successfully, FALSE otherwise
11074 */
11075 //================================================================================
11076
11077 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11078                                             const TIDSortedElemSet& theNodesNot,
11079                                             const TopoDS_Shape&     theShape )
11080 {
11081   if ( theShape.IsNull() )
11082     return false;
11083
11084   const double aTol = Precision::Confusion();
11085   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11086   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11087   if ( theShape.ShapeType() == TopAbs_SOLID )
11088   {
11089     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11090     bsc3d->PerformInfinitePoint(aTol);
11091   }
11092   else if (theShape.ShapeType() == TopAbs_FACE )
11093   {
11094     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11095   }
11096
11097   // iterates on indicated elements and get elements by back references from their nodes
11098   TIDSortedElemSet anAffected;
11099   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11100   for ( ;  elemItr != theElems.end(); ++elemItr )
11101   {
11102     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11103     if (!anElem)
11104       continue;
11105
11106     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11107     while ( nodeItr->more() )
11108     {
11109       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11110       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11111         continue;
11112       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11113       while ( backElemItr->more() )
11114       {
11115         const SMDS_MeshElement* curElem = backElemItr->next();
11116         if ( curElem && theElems.find(curElem) == theElems.end() &&
11117              ( bsc3d ?
11118                isInside( curElem, *bsc3d, aTol ) :
11119                isInside( curElem, *aFaceClassifier, aTol )))
11120           anAffected.insert( curElem );
11121       }
11122     }
11123   }
11124   return DoubleNodes( theElems, theNodesNot, anAffected );
11125 }
11126
11127 /*!
11128  *  \brief compute an oriented angle between two planes defined by four points.
11129  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11130  *  @param p0 base of the rotation axe
11131  *  @param p1 extremity of the rotation axe
11132  *  @param g1 belongs to the first plane
11133  *  @param g2 belongs to the second plane
11134  */
11135 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11136 {
11137   gp_Vec vref(p0, p1);
11138   gp_Vec v1(p0, g1);
11139   gp_Vec v2(p0, g2);
11140   gp_Vec n1 = vref.Crossed(v1);
11141   gp_Vec n2 = vref.Crossed(v2);
11142   try {
11143     return n2.AngleWithRef(n1, vref);
11144   }
11145   catch ( Standard_Failure& ) {
11146   }
11147   return Max( v1.Magnitude(), v2.Magnitude() );
11148 }
11149
11150 /*!
11151  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11152  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11153  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11154  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11155  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11156  * 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.
11157  * 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.
11158  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11159  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11160  * \param theElems - list of groups of volumes, where a group of volume is a set of
11161  *        SMDS_MeshElements sorted by Id.
11162  * \param createJointElems - if TRUE, create the elements
11163  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11164  *        the boundary between \a theDomains and the rest mesh
11165  * \return TRUE if operation has been completed successfully, FALSE otherwise
11166  */
11167 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11168                                                      bool                                 createJointElems,
11169                                                      bool                                 onAllBoundaries)
11170 {
11171   // MESSAGE("----------------------------------------------");
11172   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11173   // MESSAGE("----------------------------------------------");
11174
11175   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11176   meshDS->BuildDownWardConnectivity(true);
11177   CHRONO(50);
11178   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11179
11180   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11181   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11182   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11183
11184   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11185   std::map<int,int> celldom; // cell vtkId --> domain
11186   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11187   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11188
11189   //MESSAGE(".. Number of domains :"<<theElems.size());
11190
11191   TIDSortedElemSet theRestDomElems;
11192   const int iRestDom  = -1;
11193   const int idom0     = onAllBoundaries ? iRestDom : 0;
11194   const int nbDomains = theElems.size();
11195
11196   // Check if the domains do not share an element
11197   for (int idom = 0; idom < nbDomains-1; idom++)
11198   {
11199     //       MESSAGE("... Check of domain #" << idom);
11200     const TIDSortedElemSet& domain = theElems[idom];
11201     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11202     for (; elemItr != domain.end(); ++elemItr)
11203     {
11204       const SMDS_MeshElement* anElem = *elemItr;
11205       int idombisdeb = idom + 1 ;
11206       // check if the element belongs to a domain further in the list
11207       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11208       {
11209         const TIDSortedElemSet& domainbis = theElems[idombis];
11210         if ( domainbis.count( anElem ))
11211         {
11212           MESSAGE(".... Domain #" << idom);
11213           MESSAGE(".... Domain #" << idombis);
11214           throw SALOME_Exception("The domains are not disjoint.");
11215           return false ;
11216         }
11217       }
11218     }
11219   }
11220
11221   for (int idom = 0; idom < nbDomains; idom++)
11222   {
11223
11224     // --- build a map (face to duplicate --> volume to modify)
11225     //     with all the faces shared by 2 domains (group of elements)
11226     //     and corresponding volume of this domain, for each shared face.
11227     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11228
11229     //MESSAGE("... Neighbors of domain #" << idom);
11230     const TIDSortedElemSet& domain = theElems[idom];
11231     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11232     for (; elemItr != domain.end(); ++elemItr)
11233     {
11234       const SMDS_MeshElement* anElem = *elemItr;
11235       if (!anElem)
11236         continue;
11237       vtkIdType vtkId = anElem->GetVtkID();
11238       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11239       int neighborsVtkIds[NBMAXNEIGHBORS];
11240       int downIds[NBMAXNEIGHBORS];
11241       unsigned char downTypes[NBMAXNEIGHBORS];
11242       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11243       for (int n = 0; n < nbNeighbors; n++)
11244       {
11245         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11246         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11247         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11248         {
11249           bool ok = false;
11250           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11251           {
11252             // MESSAGE("Domain " << idombis);
11253             const TIDSortedElemSet& domainbis = theElems[idombis];
11254             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11255           }
11256           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11257           {
11258             DownIdType face(downIds[n], downTypes[n]);
11259             if (!faceDomains[face].count(idom))
11260             {
11261               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11262               celldom[vtkId] = idom;
11263               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11264             }
11265             if ( !ok )
11266             {
11267               theRestDomElems.insert( elem );
11268               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11269               celldom[neighborsVtkIds[n]] = iRestDom;
11270             }
11271           }
11272         }
11273       }
11274     }
11275   }
11276
11277   //MESSAGE("Number of shared faces " << faceDomains.size());
11278   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11279
11280   // --- explore the shared faces domain by domain,
11281   //     explore the nodes of the face and see if they belong to a cell in the domain,
11282   //     which has only a node or an edge on the border (not a shared face)
11283
11284   for (int idomain = idom0; idomain < nbDomains; idomain++)
11285   {
11286     //MESSAGE("Domain " << idomain);
11287     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11288     itface = faceDomains.begin();
11289     for (; itface != faceDomains.end(); ++itface)
11290     {
11291       const std::map<int, int>& domvol = itface->second;
11292       if (!domvol.count(idomain))
11293         continue;
11294       DownIdType face = itface->first;
11295       //MESSAGE(" --- face " << face.cellId);
11296       std::set<int> oldNodes;
11297       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11298       std::set<int>::iterator itn = oldNodes.begin();
11299       for (; itn != oldNodes.end(); ++itn)
11300       {
11301         int oldId = *itn;
11302         //MESSAGE("     node " << oldId);
11303         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11304         for (int i=0; i<l.ncells; i++)
11305         {
11306           int vtkId = l.cells[i];
11307           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11308           if (!domain.count(anElem))
11309             continue;
11310           int vtkType = grid->GetCellType(vtkId);
11311           int downId = grid->CellIdToDownId(vtkId);
11312           if (downId < 0)
11313           {
11314             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11315             continue; // not OK at this stage of the algorithm:
11316             //no cells created after BuildDownWardConnectivity
11317           }
11318           DownIdType aCell(downId, vtkType);
11319           cellDomains[aCell][idomain] = vtkId;
11320           celldom[vtkId] = idomain;
11321           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11322         }
11323       }
11324     }
11325   }
11326
11327   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11328   //     for each shared face, get the nodes
11329   //     for each node, for each domain of the face, create a clone of the node
11330
11331   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11332   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11333   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11334
11335   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11336   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11337   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11338
11339   //MESSAGE(".. Duplication of the nodes");
11340   for (int idomain = idom0; idomain < nbDomains; idomain++)
11341   {
11342     itface = faceDomains.begin();
11343     for (; itface != faceDomains.end(); ++itface)
11344     {
11345       const std::map<int, int>& domvol = itface->second;
11346       if (!domvol.count(idomain))
11347         continue;
11348       DownIdType face = itface->first;
11349       //MESSAGE(" --- face " << face.cellId);
11350       std::set<int> oldNodes;
11351       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11352       std::set<int>::iterator itn = oldNodes.begin();
11353       for (; itn != oldNodes.end(); ++itn)
11354       {
11355         int oldId = *itn;
11356         if (nodeDomains[oldId].empty())
11357         {
11358           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11359           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11360         }
11361         std::map<int, int>::const_iterator itdom = domvol.begin();
11362         for (; itdom != domvol.end(); ++itdom)
11363         {
11364           int idom = itdom->first;
11365           //MESSAGE("         domain " << idom);
11366           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11367           {
11368             if (nodeDomains[oldId].size() >= 2) // a multiple node
11369             {
11370               vector<int> orderedDoms;
11371               //MESSAGE("multiple node " << oldId);
11372               if (mutipleNodes.count(oldId))
11373                 orderedDoms = mutipleNodes[oldId];
11374               else
11375               {
11376                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11377                 for (; it != nodeDomains[oldId].end(); ++it)
11378                   orderedDoms.push_back(it->first);
11379               }
11380               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11381               //stringstream txt;
11382               //for (int i=0; i<orderedDoms.size(); i++)
11383               //  txt << orderedDoms[i] << " ";
11384               //MESSAGE("orderedDoms " << txt.str());
11385               mutipleNodes[oldId] = orderedDoms;
11386             }
11387             double *coords = grid->GetPoint(oldId);
11388             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11389             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11390             int newId = newNode->GetVtkID();
11391             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11392             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11393           }
11394         }
11395       }
11396     }
11397   }
11398
11399   //MESSAGE(".. Creation of elements");
11400   for (int idomain = idom0; idomain < nbDomains; idomain++)
11401   {
11402     itface = faceDomains.begin();
11403     for (; itface != faceDomains.end(); ++itface)
11404     {
11405       std::map<int, int> domvol = itface->second;
11406       if (!domvol.count(idomain))
11407         continue;
11408       DownIdType face = itface->first;
11409       //MESSAGE(" --- face " << face.cellId);
11410       std::set<int> oldNodes;
11411       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11412       int nbMultipleNodes = 0;
11413       std::set<int>::iterator itn = oldNodes.begin();
11414       for (; itn != oldNodes.end(); ++itn)
11415       {
11416         int oldId = *itn;
11417         if (mutipleNodes.count(oldId))
11418           nbMultipleNodes++;
11419       }
11420       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11421       {
11422         //MESSAGE("multiple Nodes detected on a shared face");
11423         int downId = itface->first.cellId;
11424         unsigned char cellType = itface->first.cellType;
11425         // --- shared edge or shared face ?
11426         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11427         {
11428           int nodes[3];
11429           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11430           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11431             if (mutipleNodes.count(nodes[i]))
11432               if (!mutipleNodesToFace.count(nodes[i]))
11433                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11434         }
11435         else // shared face (between two volumes)
11436         {
11437           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11438           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11439           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11440           for (int ie =0; ie < nbEdges; ie++)
11441           {
11442             int nodes[3];
11443             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11444             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11445             {
11446               vector<int> vn0 = mutipleNodes[nodes[0]];
11447               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11448               vector<int> doms;
11449               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11450                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11451                   if ( vn0[i0] == vn1[i1] )
11452                     doms.push_back( vn0[ i0 ]);
11453               if ( doms.size() > 2 )
11454               {
11455                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11456                 double *coords = grid->GetPoint(nodes[0]);
11457                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11458                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11459                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11460                 gp_Pnt gref;
11461                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11462                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11463                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11464                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11465                 for ( size_t id = 0; id < doms.size(); id++ )
11466                 {
11467                   int idom = doms[id];
11468                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11469                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11470                   {
11471                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11472                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11473                     if (domain.count(elem))
11474                     {
11475                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11476                       domvol[idom] = (SMDS_MeshVolume*) svol;
11477                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11478                       double values[3] = { 0,0,0 };
11479                       vtkIdType npts = 0;
11480                       vtkIdType const *pts(nullptr);
11481                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11482                       for ( vtkIdType i = 0; i < npts; ++i )
11483                       {
11484                         double *coords = grid->GetPoint( pts[i] );
11485                         for ( int j = 0; j < 3; ++j )
11486                           values[j] += coords[j] / npts;
11487                       }
11488                       if ( id == 0 )
11489                       {
11490                         gref.SetCoord( values[0], values[1], values[2] );
11491                         angleDom[idom] = 0;
11492                       }
11493                       else
11494                       {
11495                         gp_Pnt g( values[0], values[1], values[2] );
11496                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11497                         //MESSAGE("  angle=" << angleDom[idom]);
11498                       }
11499                       break;
11500                     }
11501                   }
11502                 }
11503                 map<double, int> sortedDom; // sort domains by angle
11504                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11505                   sortedDom[ia->second] = ia->first;
11506                 vector<int> vnodes;
11507                 vector<int> vdom;
11508                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11509                 {
11510                   vdom.push_back(ib->second);
11511                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11512                 }
11513                 for (int ino = 0; ino < nbNodes; ino++)
11514                   vnodes.push_back(nodes[ino]);
11515                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11516               }
11517             }
11518           }
11519         }
11520       }
11521     }
11522   }
11523
11524   // --- iterate on shared faces (volumes to modify, face to extrude)
11525   //     get node id's of the face (id SMDS = id VTK)
11526   //     create flat element with old and new nodes if requested
11527
11528   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11529   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11530
11531   std::map<int, std::map<long,int> > nodeQuadDomains;
11532   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11533
11534   //MESSAGE(".. Creation of elements: simple junction");
11535   if ( createJointElems )
11536   {
11537     string joints2DName = "joints2D";
11538     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11539     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11540     string joints3DName = "joints3D";
11541     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11542     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11543
11544     itface = faceDomains.begin();
11545     for (; itface != faceDomains.end(); ++itface)
11546     {
11547       DownIdType face = itface->first;
11548       std::set<int> oldNodes;
11549       std::set<int>::iterator itn;
11550       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11551
11552       std::map<int, int>          domvol = itface->second;
11553       std::map<int, int>::iterator itdom = domvol.begin();
11554       int     dom1 = itdom->first;
11555       int vtkVolId = itdom->second;
11556       itdom++;
11557       int           dom2 = itdom->first;
11558       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11559                                                        nodeQuadDomains);
11560       stringstream grpname;
11561       grpname << "j_";
11562       if (dom1 < dom2)
11563         grpname << dom1 << "_" << dom2;
11564       else
11565         grpname << dom2 << "_" << dom1;
11566       string namegrp = grpname.str();
11567       if (!mapOfJunctionGroups.count(namegrp))
11568         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11569       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11570       if (sgrp)
11571         sgrp->Add(vol->GetID());
11572       if (vol->GetType() == SMDSAbs_Volume)
11573         joints3DGrp->Add(vol->GetID());
11574       else if (vol->GetType() == SMDSAbs_Face)
11575         joints2DGrp->Add(vol->GetID());
11576     }
11577   }
11578
11579   // --- create volumes on multiple domain intersection if requested
11580   //     iterate on mutipleNodesToFace
11581   //     iterate on edgesMultiDomains
11582
11583   //MESSAGE(".. Creation of elements: multiple junction");
11584   if (createJointElems)
11585   {
11586     // --- iterate on mutipleNodesToFace
11587
11588     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11589     for (; itn != mutipleNodesToFace.end(); ++itn)
11590     {
11591       int node = itn->first;
11592       vector<int> orderDom = itn->second;
11593       vector<vtkIdType> orderedNodes;
11594       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11595         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11596       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11597
11598       stringstream grpname;
11599       grpname << "m2j_";
11600       grpname << 0 << "_" << 0;
11601       string namegrp = grpname.str();
11602       if (!mapOfJunctionGroups.count(namegrp))
11603         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11604       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11605       if (sgrp)
11606         sgrp->Add(face->GetID());
11607     }
11608
11609     // --- iterate on edgesMultiDomains
11610
11611     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11612     for (; ite != edgesMultiDomains.end(); ++ite)
11613     {
11614       vector<int>    nodes = ite->first;
11615       vector<int> orderDom = ite->second;
11616       vector<vtkIdType> orderedNodes;
11617       if (nodes.size() == 2)
11618       {
11619         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11620         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11621           if ( orderDom.size() == 3 )
11622             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11623               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11624           else
11625             for (int idom = orderDom.size()-1; idom >=0; idom--)
11626               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11627         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11628
11629         string namegrp = "jointsMultiples";
11630         if (!mapOfJunctionGroups.count(namegrp))
11631           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11632         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11633         if (sgrp)
11634           sgrp->Add(vol->GetID());
11635       }
11636       else
11637       {
11638         //INFOS("Quadratic multiple joints not implemented");
11639         // TODO quadratic nodes
11640       }
11641     }
11642   }
11643
11644   // --- list the explicit faces and edges of the mesh that need to be modified,
11645   //     i.e. faces and edges built with one or more duplicated nodes.
11646   //     associate these faces or edges to their corresponding domain.
11647   //     only the first domain found is kept when a face or edge is shared
11648
11649   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11650   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11651
11652   //MESSAGE(".. Modification of elements");
11653   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11654   for (int idomain = idom0; idomain < nbDomains; idomain++)
11655   {
11656     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11657     for (; itnod != nodeDomains.end(); ++itnod)
11658     {
11659       int oldId = itnod->first;
11660       //MESSAGE("     node " << oldId);
11661       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11662       for (int i = 0; i < l.ncells; i++)
11663       {
11664         int vtkId = l.cells[i];
11665         int vtkType = grid->GetCellType(vtkId);
11666         int downId = grid->CellIdToDownId(vtkId);
11667         if (downId < 0)
11668           continue; // new cells: not to be modified
11669         DownIdType aCell(downId, vtkType);
11670         int volParents[1000];
11671         int nbvol = 0;
11672         nbvol = grid->GetParentVolumes(volParents, vtkId);
11673         if ( domainType == SMDSAbs_Volume )
11674         {
11675           nbvol = grid->GetParentVolumes(volParents, vtkId);
11676         }
11677         else // domainType == SMDSAbs_Face
11678         {
11679           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11680           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11681           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11682           for (int i=0; i< nbFaces; i++)
11683           {
11684             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11685             if (vtkFaceId >= 0)
11686               volParents[nbvol++] = vtkFaceId;
11687           }
11688         }
11689         for (int j = 0; j < nbvol; j++)
11690           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11691             if (!feDom.count(vtkId))
11692             {
11693               feDom[vtkId] = idomain;
11694               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11695               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11696               //        << " type " << vtkType << " downId " << downId);
11697             }
11698       }
11699     }
11700   }
11701
11702   // --- iterate on shared faces (volumes to modify, face to extrude)
11703   //     get node id's of the face
11704   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11705
11706   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11707   for (int m=0; m<3; m++)
11708   {
11709     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11710     itface = (*amap).begin();
11711     for (; itface != (*amap).end(); ++itface)
11712     {
11713       DownIdType face = itface->first;
11714       std::set<int> oldNodes;
11715       std::set<int>::iterator itn;
11716       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11717       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11718       std::map<int, int> localClonedNodeIds;
11719
11720       std::map<int, int> domvol = itface->second;
11721       std::map<int, int>::iterator itdom = domvol.begin();
11722       for (; itdom != domvol.end(); ++itdom)
11723       {
11724         int idom = itdom->first;
11725         int vtkVolId = itdom->second;
11726         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11727         localClonedNodeIds.clear();
11728         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11729         {
11730           int oldId = *itn;
11731           if (nodeDomains[oldId].count(idom))
11732           {
11733             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11734             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11735           }
11736         }
11737         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11738       }
11739     }
11740   }
11741
11742   // Remove empty groups (issue 0022812)
11743   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11744   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11745   {
11746     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11747       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11748   }
11749
11750   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11751   grid->DeleteLinks();
11752
11753   CHRONOSTOP(50);
11754   counters::stats();
11755   return true;
11756 }
11757
11758 /*!
11759  * \brief Double nodes on some external faces and create flat elements.
11760  * Flat elements are mainly used by some types of mechanic calculations.
11761  *
11762  * Each group of the list must be constituted of faces.
11763  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11764  * @param theElems - list of groups of faces, where a group of faces is a set of
11765  * SMDS_MeshElements sorted by Id.
11766  * @return TRUE if operation has been completed successfully, FALSE otherwise
11767  */
11768 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11769 {
11770   // MESSAGE("-------------------------------------------------");
11771   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11772   // MESSAGE("-------------------------------------------------");
11773
11774   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11775
11776   // --- For each group of faces
11777   //     duplicate the nodes, create a flat element based on the face
11778   //     replace the nodes of the faces by their clones
11779
11780   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11781   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11782   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11783
11784   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11785   {
11786     const TIDSortedElemSet&           domain = theElems[idom];
11787     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11788     for ( ; elemItr != domain.end(); ++elemItr )
11789     {
11790       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11791       if (!aFace)
11792         continue;
11793       // MESSAGE("aFace=" << aFace->GetID());
11794       bool isQuad = aFace->IsQuadratic();
11795       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11796
11797       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11798
11799       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11800       while (nodeIt->more())
11801       {
11802         const SMDS_MeshNode* node = nodeIt->next();
11803         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11804         if (isMedium)
11805           ln2.push_back(node);
11806         else
11807           ln0.push_back(node);
11808
11809         const SMDS_MeshNode* clone = 0;
11810         if (!clonedNodes.count(node))
11811         {
11812           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11813           copyPosition( node, clone );
11814           clonedNodes[node] = clone;
11815         }
11816         else
11817           clone = clonedNodes[node];
11818
11819         if (isMedium)
11820           ln3.push_back(clone);
11821         else
11822           ln1.push_back(clone);
11823
11824         const SMDS_MeshNode* inter = 0;
11825         if (isQuad && (!isMedium))
11826         {
11827           if (!intermediateNodes.count(node))
11828           {
11829             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11830             copyPosition( node, inter );
11831             intermediateNodes[node] = inter;
11832           }
11833           else
11834             inter = intermediateNodes[node];
11835           ln4.push_back(inter);
11836         }
11837       }
11838
11839       // --- extrude the face
11840
11841       vector<const SMDS_MeshNode*> ln;
11842       SMDS_MeshVolume* vol = 0;
11843       vtkIdType aType = aFace->GetVtkType();
11844       switch (aType)
11845       {
11846       case VTK_TRIANGLE:
11847         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11848         // MESSAGE("vol prism " << vol->GetID());
11849         ln.push_back(ln1[0]);
11850         ln.push_back(ln1[1]);
11851         ln.push_back(ln1[2]);
11852         break;
11853       case VTK_QUAD:
11854         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11855         // MESSAGE("vol hexa " << vol->GetID());
11856         ln.push_back(ln1[0]);
11857         ln.push_back(ln1[1]);
11858         ln.push_back(ln1[2]);
11859         ln.push_back(ln1[3]);
11860         break;
11861       case VTK_QUADRATIC_TRIANGLE:
11862         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11863                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11864         // MESSAGE("vol quad prism " << vol->GetID());
11865         ln.push_back(ln1[0]);
11866         ln.push_back(ln1[1]);
11867         ln.push_back(ln1[2]);
11868         ln.push_back(ln3[0]);
11869         ln.push_back(ln3[1]);
11870         ln.push_back(ln3[2]);
11871         break;
11872       case VTK_QUADRATIC_QUAD:
11873         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11874         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11875         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11876         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11877                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11878                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11879         // MESSAGE("vol quad hexa " << vol->GetID());
11880         ln.push_back(ln1[0]);
11881         ln.push_back(ln1[1]);
11882         ln.push_back(ln1[2]);
11883         ln.push_back(ln1[3]);
11884         ln.push_back(ln3[0]);
11885         ln.push_back(ln3[1]);
11886         ln.push_back(ln3[2]);
11887         ln.push_back(ln3[3]);
11888         break;
11889       case VTK_POLYGON:
11890         break;
11891       default:
11892         break;
11893       }
11894
11895       if (vol)
11896       {
11897         stringstream grpname;
11898         grpname << "jf_";
11899         grpname << idom;
11900         string namegrp = grpname.str();
11901         if (!mapOfJunctionGroups.count(namegrp))
11902           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11903         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11904         if (sgrp)
11905           sgrp->Add(vol->GetID());
11906       }
11907
11908       // --- modify the face
11909
11910       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11911     }
11912   }
11913   return true;
11914 }
11915
11916 /*!
11917  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11918  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11919  *  groups of faces to remove inside the object, (idem edges).
11920  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11921  */
11922 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11923                                       const TopoDS_Shape&             theShape,
11924                                       SMESH_NodeSearcher*             theNodeSearcher,
11925                                       const char*                     groupName,
11926                                       std::vector<double>&            nodesCoords,
11927                                       std::vector<std::vector<int> >& listOfListOfNodes)
11928 {
11929   // MESSAGE("--------------------------------");
11930   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11931   // MESSAGE("--------------------------------");
11932
11933   // --- zone of volumes to remove is given :
11934   //     1 either by a geom shape (one or more vertices) and a radius,
11935   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11936   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11937   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11938   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11939   //     defined by it's name.
11940
11941   SMESHDS_GroupBase* groupDS = 0;
11942   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11943   while ( groupIt->more() )
11944   {
11945     groupDS = 0;
11946     SMESH_Group * group = groupIt->next();
11947     if ( !group ) continue;
11948     groupDS = group->GetGroupDS();
11949     if ( !groupDS || groupDS->IsEmpty() ) continue;
11950     std::string grpName = group->GetName();
11951     //MESSAGE("grpName=" << grpName);
11952     if (grpName == groupName)
11953       break;
11954     else
11955       groupDS = 0;
11956   }
11957
11958   bool isNodeGroup = false;
11959   bool isNodeCoords = false;
11960   if (groupDS)
11961   {
11962     if (groupDS->GetType() != SMDSAbs_Node)
11963       return;
11964     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11965   }
11966
11967   if (nodesCoords.size() > 0)
11968     isNodeCoords = true; // a list o nodes given by their coordinates
11969   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11970
11971   // --- define groups to build
11972
11973   // --- group of SMDS volumes
11974   string grpvName = groupName;
11975   grpvName += "_vol";
11976   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11977   if (!grp)
11978   {
11979     MESSAGE("group not created " << grpvName);
11980     return;
11981   }
11982   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11983
11984   // --- group of SMDS faces on the skin
11985   string grpsName = groupName;
11986   grpsName += "_skin";
11987   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11988   if (!grps)
11989   {
11990     MESSAGE("group not created " << grpsName);
11991     return;
11992   }
11993   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11994
11995   // --- group of SMDS faces internal (several shapes)
11996   string grpiName = groupName;
11997   grpiName += "_internalFaces";
11998   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11999   if (!grpi)
12000   {
12001     MESSAGE("group not created " << grpiName);
12002     return;
12003   }
12004   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12005
12006   // --- group of SMDS faces internal (several shapes)
12007   string grpeiName = groupName;
12008   grpeiName += "_internalEdges";
12009   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12010   if (!grpei)
12011   {
12012     MESSAGE("group not created " << grpeiName);
12013     return;
12014   }
12015   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12016
12017   // --- build downward connectivity
12018
12019   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12020   meshDS->BuildDownWardConnectivity(true);
12021   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12022
12023   // --- set of volumes detected inside
12024
12025   std::set<int> setOfInsideVol;
12026   std::set<int> setOfVolToCheck;
12027
12028   std::vector<gp_Pnt> gpnts;
12029
12030   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12031   {
12032     //MESSAGE("group of nodes provided");
12033     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12034     while ( elemIt->more() )
12035     {
12036       const SMDS_MeshElement* elem = elemIt->next();
12037       if (!elem)
12038         continue;
12039       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12040       if (!node)
12041         continue;
12042       SMDS_MeshElement* vol = 0;
12043       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12044       while (volItr->more())
12045       {
12046         vol = (SMDS_MeshElement*)volItr->next();
12047         setOfInsideVol.insert(vol->GetVtkID());
12048         sgrp->Add(vol->GetID());
12049       }
12050     }
12051   }
12052   else if (isNodeCoords)
12053   {
12054     //MESSAGE("list of nodes coordinates provided");
12055     size_t i = 0;
12056     int k = 0;
12057     while ( i < nodesCoords.size()-2 )
12058     {
12059       double x = nodesCoords[i++];
12060       double y = nodesCoords[i++];
12061       double z = nodesCoords[i++];
12062       gp_Pnt p = gp_Pnt(x, y ,z);
12063       gpnts.push_back(p);
12064       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12065       k++;
12066     }
12067   }
12068   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12069   {
12070     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12071     TopTools_IndexedMapOfShape vertexMap;
12072     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12073     gp_Pnt p = gp_Pnt(0,0,0);
12074     if (vertexMap.Extent() < 1)
12075       return;
12076
12077     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12078     {
12079       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12080       p = BRep_Tool::Pnt(vertex);
12081       gpnts.push_back(p);
12082       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12083     }
12084   }
12085
12086   if (gpnts.size() > 0)
12087   {
12088     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12089     //MESSAGE("startNode->nodeId " << nodeId);
12090
12091     double radius2 = radius*radius;
12092     //MESSAGE("radius2 " << radius2);
12093
12094     // --- volumes on start node
12095
12096     setOfVolToCheck.clear();
12097     SMDS_MeshElement* startVol = 0;
12098     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12099     while (volItr->more())
12100     {
12101       startVol = (SMDS_MeshElement*)volItr->next();
12102       setOfVolToCheck.insert(startVol->GetVtkID());
12103     }
12104     if (setOfVolToCheck.empty())
12105     {
12106       MESSAGE("No volumes found");
12107       return;
12108     }
12109
12110     // --- starting with central volumes then their neighbors, check if they are inside
12111     //     or outside the domain, until no more new neighbor volume is inside.
12112     //     Fill the group of inside volumes
12113
12114     std::map<int, double> mapOfNodeDistance2;
12115     std::set<int> setOfOutsideVol;
12116     while (!setOfVolToCheck.empty())
12117     {
12118       std::set<int>::iterator it = setOfVolToCheck.begin();
12119       int vtkId = *it;
12120       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12121       bool volInside = false;
12122       vtkIdType npts = 0;
12123       vtkIdType const *pts(nullptr);
12124       grid->GetCellPoints(vtkId, npts, pts);
12125       for (int i=0; i<npts; i++)
12126       {
12127         double distance2 = 0;
12128         if (mapOfNodeDistance2.count(pts[i]))
12129         {
12130           distance2 = mapOfNodeDistance2[pts[i]];
12131           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12132         }
12133         else
12134         {
12135           double *coords = grid->GetPoint(pts[i]);
12136           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12137           distance2 = 1.E40;
12138           for ( size_t j = 0; j < gpnts.size(); j++ )
12139           {
12140             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12141             if (d2 < distance2)
12142             {
12143               distance2 = d2;
12144               if (distance2 < radius2)
12145                 break;
12146             }
12147           }
12148           mapOfNodeDistance2[pts[i]] = distance2;
12149           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12150         }
12151         if (distance2 < radius2)
12152         {
12153           volInside = true; // one or more nodes inside the domain
12154           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12155           break;
12156         }
12157       }
12158       if (volInside)
12159       {
12160         setOfInsideVol.insert(vtkId);
12161         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12162         int neighborsVtkIds[NBMAXNEIGHBORS];
12163         int downIds[NBMAXNEIGHBORS];
12164         unsigned char downTypes[NBMAXNEIGHBORS];
12165         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12166         for (int n = 0; n < nbNeighbors; n++)
12167           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12168             setOfVolToCheck.insert(neighborsVtkIds[n]);
12169       }
12170       else
12171       {
12172         setOfOutsideVol.insert(vtkId);
12173         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12174       }
12175       setOfVolToCheck.erase(vtkId);
12176     }
12177   }
12178
12179   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12180   //     If yes, add the volume to the inside set
12181
12182   bool addedInside = true;
12183   std::set<int> setOfVolToReCheck;
12184   while (addedInside)
12185   {
12186     //MESSAGE(" --------------------------- re check");
12187     addedInside = false;
12188     std::set<int>::iterator itv = setOfInsideVol.begin();
12189     for (; itv != setOfInsideVol.end(); ++itv)
12190     {
12191       int vtkId = *itv;
12192       int neighborsVtkIds[NBMAXNEIGHBORS];
12193       int downIds[NBMAXNEIGHBORS];
12194       unsigned char downTypes[NBMAXNEIGHBORS];
12195       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12196       for (int n = 0; n < nbNeighbors; n++)
12197         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12198           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12199     }
12200     setOfVolToCheck = setOfVolToReCheck;
12201     setOfVolToReCheck.clear();
12202     while  (!setOfVolToCheck.empty())
12203     {
12204       std::set<int>::iterator it = setOfVolToCheck.begin();
12205       int vtkId = *it;
12206       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12207       {
12208         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12209         int countInside = 0;
12210         int neighborsVtkIds[NBMAXNEIGHBORS];
12211         int downIds[NBMAXNEIGHBORS];
12212         unsigned char downTypes[NBMAXNEIGHBORS];
12213         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214         for (int n = 0; n < nbNeighbors; n++)
12215           if (setOfInsideVol.count(neighborsVtkIds[n]))
12216             countInside++;
12217         //MESSAGE("countInside " << countInside);
12218         if (countInside > 1)
12219         {
12220           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12221           setOfInsideVol.insert(vtkId);
12222           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12223           addedInside = true;
12224         }
12225         else
12226           setOfVolToReCheck.insert(vtkId);
12227       }
12228       setOfVolToCheck.erase(vtkId);
12229     }
12230   }
12231
12232   // --- map of Downward faces at the boundary, inside the global volume
12233   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12234   //     fill group of SMDS faces inside the volume (when several volume shapes)
12235   //     fill group of SMDS faces on the skin of the global volume (if skin)
12236
12237   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12238   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12239   std::set<int>::iterator it = setOfInsideVol.begin();
12240   for (; it != setOfInsideVol.end(); ++it)
12241   {
12242     int vtkId = *it;
12243     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12244     int neighborsVtkIds[NBMAXNEIGHBORS];
12245     int downIds[NBMAXNEIGHBORS];
12246     unsigned char downTypes[NBMAXNEIGHBORS];
12247     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12248     for (int n = 0; n < nbNeighbors; n++)
12249     {
12250       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12251       if (neighborDim == 3)
12252       {
12253         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12254         {
12255           DownIdType face(downIds[n], downTypes[n]);
12256           boundaryFaces[face] = vtkId;
12257         }
12258         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12259         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12260         if (vtkFaceId >= 0)
12261         {
12262           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12263           // find also the smds edges on this face
12264           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12265           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12266           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12267           for (int i = 0; i < nbEdges; i++)
12268           {
12269             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12270             if (vtkEdgeId >= 0)
12271               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12272           }
12273         }
12274       }
12275       else if (neighborDim == 2) // skin of the volume
12276       {
12277         DownIdType face(downIds[n], downTypes[n]);
12278         skinFaces[face] = vtkId;
12279         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12280         if (vtkFaceId >= 0)
12281           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12282       }
12283     }
12284   }
12285
12286   // --- identify the edges constituting the wire of each subshape on the skin
12287   //     define polylines with the nodes of edges, equivalent to wires
12288   //     project polylines on subshapes, and partition, to get geom faces
12289
12290   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12291   std::set<int>                 shapeIds;
12292
12293   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12294   while (itelem->more())
12295   {
12296     const SMDS_MeshElement *elem = itelem->next();
12297     int shapeId = elem->getshapeId();
12298     int   vtkId = elem->GetVtkID();
12299     if (!shapeIdToVtkIdSet.count(shapeId))
12300     {
12301       shapeIds.insert(shapeId);
12302     }
12303     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12304   }
12305
12306   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12307   std::set<DownIdType, DownIdCompare> emptyEdges;
12308
12309   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12310   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12311   {
12312     int shapeId = itShape->first;
12313     //MESSAGE(" --- Shape ID --- "<< shapeId);
12314     shapeIdToEdges[shapeId] = emptyEdges;
12315
12316     std::vector<int> nodesEdges;
12317
12318     std::set<int>::iterator its = itShape->second.begin();
12319     for (; its != itShape->second.end(); ++its)
12320     {
12321       int vtkId = *its;
12322       //MESSAGE("     " << vtkId);
12323       int neighborsVtkIds[NBMAXNEIGHBORS];
12324       int downIds[NBMAXNEIGHBORS];
12325       unsigned char downTypes[NBMAXNEIGHBORS];
12326       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12327       for (int n = 0; n < nbNeighbors; n++)
12328       {
12329         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12330           continue;
12331         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12332         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12333         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12334         {
12335           DownIdType edge(downIds[n], downTypes[n]);
12336           if (!shapeIdToEdges[shapeId].count(edge))
12337           {
12338             shapeIdToEdges[shapeId].insert(edge);
12339             int vtkNodeId[3];
12340             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12341             nodesEdges.push_back(vtkNodeId[0]);
12342             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12343             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12344           }
12345         }
12346       }
12347     }
12348
12349     std::list<int> order;
12350     if (nodesEdges.size() > 0)
12351     {
12352       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12353       nodesEdges[0] = -1;
12354       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12355       nodesEdges[1] = -1; // do not reuse this edge
12356       bool found = true;
12357       while (found)
12358       {
12359         int nodeTofind = order.back(); // try first to push back
12360         int i = 0;
12361         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12362           if (nodesEdges[i] == nodeTofind)
12363             break;
12364         if ( i == (int) nodesEdges.size() )
12365           found = false; // no follower found on back
12366         else
12367         {
12368           if (i%2) // odd ==> use the previous one
12369             if (nodesEdges[i-1] < 0)
12370               found = false;
12371             else
12372             {
12373               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12374               nodesEdges[i-1] = -1;
12375             }
12376           else // even ==> use the next one
12377             if (nodesEdges[i+1] < 0)
12378               found = false;
12379             else
12380             {
12381               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12382               nodesEdges[i+1] = -1;
12383             }
12384         }
12385         if (found)
12386           continue;
12387         // try to push front
12388         found = true;
12389         nodeTofind = order.front(); // try to push front
12390         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12391           if ( nodesEdges[i] == nodeTofind )
12392             break;
12393         if ( i == (int)nodesEdges.size() )
12394         {
12395           found = false; // no predecessor found on front
12396           continue;
12397         }
12398         if (i%2) // odd ==> use the previous one
12399           if (nodesEdges[i-1] < 0)
12400             found = false;
12401           else
12402           {
12403             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12404             nodesEdges[i-1] = -1;
12405           }
12406         else // even ==> use the next one
12407           if (nodesEdges[i+1] < 0)
12408             found = false;
12409           else
12410           {
12411             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12412             nodesEdges[i+1] = -1;
12413           }
12414       }
12415     }
12416
12417
12418     std::vector<int> nodes;
12419     nodes.push_back(shapeId);
12420     std::list<int>::iterator itl = order.begin();
12421     for (; itl != order.end(); itl++)
12422     {
12423       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12424       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12425     }
12426     listOfListOfNodes.push_back(nodes);
12427   }
12428
12429   //     partition geom faces with blocFissure
12430   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12431   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12432
12433   return;
12434 }
12435
12436
12437 //================================================================================
12438 /*!
12439  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12440  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12441  * \return TRUE if operation has been completed successfully, FALSE otherwise
12442  */
12443 //================================================================================
12444
12445 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12446 {
12447   // iterates on volume elements and detect all free faces on them
12448   SMESHDS_Mesh* aMesh = GetMeshDS();
12449   if (!aMesh)
12450     return false;
12451
12452   ElemFeatures faceType( SMDSAbs_Face );
12453   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12454   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12455   while(vIt->more())
12456   {
12457     const SMDS_MeshVolume* volume = vIt->next();
12458     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12459     vTool.SetExternalNormal();
12460     const int iQuad = volume->IsQuadratic();
12461     faceType.SetQuad( iQuad );
12462     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12463     {
12464       if (!vTool.IsFreeFace(iface))
12465         continue;
12466       nbFree++;
12467       vector<const SMDS_MeshNode *> nodes;
12468       int nbFaceNodes = vTool.NbFaceNodes(iface);
12469       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12470       int inode = 0;
12471       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12472         nodes.push_back(faceNodes[inode]);
12473
12474       if (iQuad) // add medium nodes
12475       {
12476         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12477           nodes.push_back(faceNodes[inode]);
12478         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12479           nodes.push_back(faceNodes[8]);
12480       }
12481       // add new face based on volume nodes
12482       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12483       {
12484         nbExisted++; // face already exists
12485       }
12486       else
12487       {
12488         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12489         nbCreated++;
12490       }
12491     }
12492   }
12493   return ( nbFree == ( nbExisted + nbCreated ));
12494 }
12495
12496 namespace
12497 {
12498   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12499   {
12500     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12501       return n;
12502     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12503   }
12504 }
12505 //================================================================================
12506 /*!
12507  * \brief Creates missing boundary elements
12508  *  \param elements - elements whose boundary is to be checked
12509  *  \param dimension - defines type of boundary elements to create
12510  *  \param group - a group to store created boundary elements in
12511  *  \param targetMesh - a mesh to store created boundary elements in
12512  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12513  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12514  *                                boundary elements will be copied into the targetMesh
12515  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12516  *                                boundary elements will be added into the new group
12517  *  \param aroundElements - if true, elements will be created on boundary of given
12518  *                          elements else, on boundary of the whole mesh.
12519  * \return nb of added boundary elements
12520  */
12521 //================================================================================
12522
12523 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12524                                        Bnd_Dimension           dimension,
12525                                        SMESH_Group*            group/*=0*/,
12526                                        SMESH_Mesh*             targetMesh/*=0*/,
12527                                        bool                    toCopyElements/*=false*/,
12528                                        bool                    toCopyExistingBoundary/*=false*/,
12529                                        bool                    toAddExistingBondary/*= false*/,
12530                                        bool                    aroundElements/*= false*/)
12531 {
12532   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12533   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12534   // hope that all elements are of the same type, do not check them all
12535   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12536     throw SALOME_Exception(LOCALIZED("wrong element type"));
12537
12538   if ( !targetMesh )
12539     toCopyElements = toCopyExistingBoundary = false;
12540
12541   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12542   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12543   int nbAddedBnd = 0;
12544
12545   // editor adding present bnd elements and optionally holding elements to add to the group
12546   SMESH_MeshEditor* presentEditor;
12547   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12548   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12549
12550   SMESH_MesherHelper helper( *myMesh );
12551   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12552   SMDS_VolumeTool vTool;
12553   TIDSortedElemSet avoidSet;
12554   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12555   size_t inode;
12556
12557   typedef vector<const SMDS_MeshNode*> TConnectivity;
12558   TConnectivity tgtNodes;
12559   ElemFeatures elemKind( missType ), elemToCopy;
12560
12561   vector<const SMDS_MeshElement*> presentBndElems;
12562   vector<TConnectivity>           missingBndElems;
12563   vector<int>                     freeFacets;
12564   TConnectivity nodes, elemNodes;
12565
12566   SMDS_ElemIteratorPtr eIt;
12567   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12568   else                  eIt = SMESHUtils::elemSetIterator( elements );
12569
12570   while ( eIt->more() )
12571   {
12572     const SMDS_MeshElement* elem = eIt->next();
12573     const int              iQuad = elem->IsQuadratic();
12574     elemKind.SetQuad( iQuad );
12575
12576     // ------------------------------------------------------------------------------------
12577     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12578     // ------------------------------------------------------------------------------------
12579     presentBndElems.clear();
12580     missingBndElems.clear();
12581     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12582     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12583     {
12584       const SMDS_MeshElement* otherVol = 0;
12585       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12586       {
12587         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12588              ( !aroundElements || elements.count( otherVol )))
12589           continue;
12590         freeFacets.push_back( iface );
12591       }
12592       if ( missType == SMDSAbs_Face )
12593         vTool.SetExternalNormal();
12594       for ( size_t i = 0; i < freeFacets.size(); ++i )
12595       {
12596         int                iface = freeFacets[i];
12597         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12598         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12599         if ( missType == SMDSAbs_Edge ) // boundary edges
12600         {
12601           nodes.resize( 2+iQuad );
12602           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12603           {
12604             for ( size_t j = 0; j < nodes.size(); ++j )
12605               nodes[ j ] = nn[ i+j ];
12606             if ( const SMDS_MeshElement* edge =
12607                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12608               presentBndElems.push_back( edge );
12609             else
12610               missingBndElems.push_back( nodes );
12611           }
12612         }
12613         else // boundary face
12614         {
12615           nodes.clear();
12616           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12617             nodes.push_back( nn[inode] ); // add corner nodes
12618           if (iQuad)
12619             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12620               nodes.push_back( nn[inode] ); // add medium nodes
12621           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12622           if ( iCenter > 0 )
12623             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12624
12625           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12626                                                                SMDSAbs_Face, /*noMedium=*/false ))
12627             presentBndElems.push_back( f );
12628           else
12629             missingBndElems.push_back( nodes );
12630
12631           if ( targetMesh != myMesh )
12632           {
12633             // add 1D elements on face boundary to be added to a new mesh
12634             const SMDS_MeshElement* edge;
12635             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12636             {
12637               if ( iQuad )
12638                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12639               else
12640                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12641               if ( edge && avoidSet.insert( edge ).second )
12642                 presentBndElems.push_back( edge );
12643             }
12644           }
12645         }
12646       }
12647     }
12648     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12649     {
12650       avoidSet.clear(), avoidSet.insert( elem );
12651       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12652                         SMDS_MeshElement::iterator() );
12653       elemNodes.push_back( elemNodes[0] );
12654       nodes.resize( 2 + iQuad );
12655       const int nbLinks = elem->NbCornerNodes();
12656       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12657       {
12658         nodes[0] = elemNodes[iN];
12659         nodes[1] = elemNodes[iN+1+iQuad];
12660         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12661           continue; // not free link
12662
12663         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12664         if ( const SMDS_MeshElement* edge =
12665              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12666           presentBndElems.push_back( edge );
12667         else
12668           missingBndElems.push_back( nodes );
12669       }
12670     }
12671
12672     // ---------------------------------
12673     // 2. Add missing boundary elements
12674     // ---------------------------------
12675     if ( targetMesh != myMesh )
12676       // instead of making a map of nodes in this mesh and targetMesh,
12677       // we create nodes with same IDs.
12678       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12679       {
12680         TConnectivity& srcNodes = missingBndElems[i];
12681         tgtNodes.resize( srcNodes.size() );
12682         for ( inode = 0; inode < srcNodes.size(); ++inode )
12683           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12684         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12685                                                                        missType,
12686                                                                        /*noMedium=*/false))
12687           continue;
12688         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12689         ++nbAddedBnd;
12690       }
12691     else
12692       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12693       {
12694         TConnectivity& nodes = missingBndElems[ i ];
12695         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12696                                                                        missType,
12697                                                                        /*noMedium=*/false))
12698           continue;
12699         SMDS_MeshElement* newElem =
12700           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12701         nbAddedBnd += bool( newElem );
12702
12703         // try to set a new element to a shape
12704         if ( myMesh->HasShapeToMesh() )
12705         {
12706           bool ok = true;
12707           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12708           const size_t nbN = nodes.size() / (iQuad+1 );
12709           for ( inode = 0; inode < nbN && ok; ++inode )
12710           {
12711             pair<int, TopAbs_ShapeEnum> i_stype =
12712               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12713             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12714               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12715           }
12716           if ( ok && mediumShapes.size() > 1 )
12717           {
12718             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12719             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12720             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12721             {
12722               if (( ok = ( stype_i->first != stype_i_0.first )))
12723                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12724                                         aMesh->IndexToShape( stype_i_0.second ));
12725             }
12726           }
12727           if ( ok && mediumShapes.begin()->first == missShapeType )
12728             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12729         }
12730       }
12731
12732     // ----------------------------------
12733     // 3. Copy present boundary elements
12734     // ----------------------------------
12735     if ( toCopyExistingBoundary )
12736       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12737       {
12738         const SMDS_MeshElement* e = presentBndElems[i];
12739         tgtNodes.resize( e->NbNodes() );
12740         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12741           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12742         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12743       }
12744     else // store present elements to add them to a group
12745       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12746       {
12747         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12748       }
12749
12750   } // loop on given elements
12751
12752   // ---------------------------------------------
12753   // 4. Fill group with boundary elements
12754   // ---------------------------------------------
12755   if ( group )
12756   {
12757     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12758       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12759         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12760   }
12761   tgtEditor.myLastCreatedElems.clear();
12762   tgtEditor2.myLastCreatedElems.clear();
12763
12764   // -----------------------
12765   // 5. Copy given elements
12766   // -----------------------
12767   if ( toCopyElements && targetMesh != myMesh )
12768   {
12769     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12770     else                  eIt = SMESHUtils::elemSetIterator( elements );
12771     while (eIt->more())
12772     {
12773       const SMDS_MeshElement* elem = eIt->next();
12774       tgtNodes.resize( elem->NbNodes() );
12775       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12776         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12777       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12778
12779       tgtEditor.myLastCreatedElems.clear();
12780     }
12781   }
12782   return nbAddedBnd;
12783 }
12784
12785 //================================================================================
12786 /*!
12787  * \brief Copy node position and set \a to node on the same geometry
12788  */
12789 //================================================================================
12790
12791 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12792                                      const SMDS_MeshNode* to )
12793 {
12794   if ( !from || !to ) return;
12795
12796   SMDS_PositionPtr pos = from->GetPosition();
12797   if ( !pos || from->getshapeId() < 1 ) return;
12798
12799   switch ( pos->GetTypeOfPosition() )
12800   {
12801   case SMDS_TOP_3DSPACE: break;
12802
12803   case SMDS_TOP_FACE:
12804   {
12805     SMDS_FacePositionPtr fPos = pos;
12806     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12807                                 fPos->GetUParameter(), fPos->GetVParameter() );
12808     break;
12809   }
12810   case SMDS_TOP_EDGE:
12811   {
12812     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12813     SMDS_EdgePositionPtr ePos = pos;
12814     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12815     break;
12816   }
12817   case SMDS_TOP_VERTEX:
12818   {
12819     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12820     break;
12821   }
12822   case SMDS_TOP_UNSPEC:
12823   default:;
12824   }
12825 }