Salome HOME
Merge branch 'V9_9_BR'
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2022  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 std::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 Remove a node and fill a hole appeared, by changing surrounding faces
480  */
481 //================================================================================
482
483 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
484 {
485   if ( ! node )
486     return;
487
488   if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
489     throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
490
491   // check that only triangles surround the node
492   for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
493   {
494     const SMDS_MeshElement* face = fIt->next();
495     if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
496       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
497     if ( face->IsQuadratic() )
498       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
499   }
500
501   std::vector< const SMDS_MeshNode*> neighbours(2);
502   SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
503
504   bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
505
506   // if ( neighbours.size() == 2 ) // on boundary
507   // {
508   //   // check if theNode and neighbours are on a line
509   //   gp_Pnt pN = SMESH_NodeXYZ( node );
510   //   gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
511   //   gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
512   //   double dist01 = p0.Distance( p1 );
513   //   double    tol = 0.01 * dist01;
514   //   double  distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
515   //   bool   onLine = distN < tol;
516   //   toRemove = !onLine;
517   // }
518
519   if ( neighbours.empty() ) // not on boundary
520   {
521     TIDSortedElemSet linkedNodes;
522     GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
523     for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
524     if ( neighbours.empty() )
525       toRemove = true;
526   }
527
528   if ( toRemove )
529   {
530     this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
531     return;
532   }
533
534   // choose a node to replace by
535   const SMDS_MeshNode* nToReplace = nullptr;
536   SMESH_NodeXYZ           nodeXYZ = node;
537   double                  minDist = Precision::Infinite();
538   for ( const SMDS_MeshNode* n : neighbours )
539   {
540     double dist = nodeXYZ.SquareDistance( n );
541     if ( dist < minDist )
542     {
543       minDist = dist;
544       nToReplace = n;
545     }
546   }
547
548   // remove node + replace by nToReplace
549   std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
550   TListOfListOfNodes nodesToMerge( 1, nodeGroup );
551   this->MergeNodes( nodesToMerge );
552 }
553
554 //================================================================================
555 /*!
556  * \brief Create 0D elements on all nodes of the given object.
557  *  \param elements - Elements on whose nodes to create 0D elements; if empty,
558  *                    the all mesh is treated
559  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
560  *  \param duplicateElements - to add one more 0D element to a node or not
561  */
562 //================================================================================
563
564 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
565                                                    TIDSortedElemSet&       all0DElems,
566                                                    const bool              duplicateElements )
567 {
568   SMDS_ElemIteratorPtr elemIt;
569   if ( elements.empty() )
570   {
571     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
572   }
573   else
574   {
575     elemIt = SMESHUtils::elemSetIterator( elements );
576   }
577
578   while ( elemIt->more() )
579   {
580     const SMDS_MeshElement* e = elemIt->next();
581     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
582     while ( nodeIt->more() )
583     {
584       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
585       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
586       if ( duplicateElements || !it0D->more() )
587       {
588         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
589         all0DElems.insert( myLastCreatedElems.back() );
590       }
591       while ( it0D->more() )
592         all0DElems.insert( it0D->next() );
593     }
594   }
595 }
596
597 //=======================================================================
598 //function : FindShape
599 //purpose  : Return an index of the shape theElem is on
600 //           or zero if a shape not found
601 //=======================================================================
602
603 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
604 {
605   ClearLastCreated();
606
607   SMESHDS_Mesh * aMesh = GetMeshDS();
608   if ( aMesh->ShapeToMesh().IsNull() )
609     return 0;
610
611   int aShapeID = theElem->getshapeId();
612   if ( aShapeID < 1 )
613     return 0;
614
615   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
616     if ( sm->Contains( theElem ))
617       return aShapeID;
618
619   if ( theElem->GetType() == SMDSAbs_Node ) {
620     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
621   }
622   else {
623     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
624   }
625
626   TopoDS_Shape aShape; // the shape a node of theElem is on
627   if ( theElem->GetType() != SMDSAbs_Node )
628   {
629     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
630     while ( nodeIt->more() ) {
631       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
632       if ((aShapeID = node->getshapeId()) > 0) {
633         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
634           if ( sm->Contains( theElem ))
635             return aShapeID;
636           if ( aShape.IsNull() )
637             aShape = aMesh->IndexToShape( aShapeID );
638         }
639       }
640     }
641   }
642
643   // None of nodes is on a proper shape,
644   // find the shape among ancestors of aShape on which a node is
645   if ( !aShape.IsNull() ) {
646     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
647     for ( ; ancIt.More(); ancIt.Next() ) {
648       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
649       if ( sm && sm->Contains( theElem ))
650         return aMesh->ShapeToIndex( ancIt.Value() );
651     }
652   }
653   else
654   {
655     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
656     while ( const SMESHDS_SubMesh* sm = smIt->next() )
657       if ( sm->Contains( theElem ))
658         return sm->GetID();
659   }
660
661   return 0;
662 }
663
664 //=======================================================================
665 //function : IsMedium
666 //purpose  :
667 //=======================================================================
668
669 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
670                                 const SMDSAbs_ElementType typeToCheck)
671 {
672   bool isMedium = false;
673   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
674   while (it->more() && !isMedium ) {
675     const SMDS_MeshElement* elem = it->next();
676     isMedium = elem->IsMediumNode(node);
677   }
678   return isMedium;
679 }
680
681 //=======================================================================
682 //function : shiftNodesQuadTria
683 //purpose  : Shift nodes in the array corresponded to quadratic triangle
684 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
685 //=======================================================================
686
687 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
688 {
689   const SMDS_MeshNode* nd1 = aNodes[0];
690   aNodes[0] = aNodes[1];
691   aNodes[1] = aNodes[2];
692   aNodes[2] = nd1;
693   const SMDS_MeshNode* nd2 = aNodes[3];
694   aNodes[3] = aNodes[4];
695   aNodes[4] = aNodes[5];
696   aNodes[5] = nd2;
697 }
698
699 //=======================================================================
700 //function : getNodesFromTwoTria
701 //purpose  : 
702 //=======================================================================
703
704 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
705                                 const SMDS_MeshElement * theTria2,
706                                 vector< const SMDS_MeshNode*>& N1,
707                                 vector< const SMDS_MeshNode*>& N2)
708 {
709   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
710   if ( N1.size() < 6 ) return false;
711   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
712   if ( N2.size() < 6 ) return false;
713
714   int sames[3] = {-1,-1,-1};
715   int nbsames = 0;
716   int i, j;
717   for(i=0; i<3; i++) {
718     for(j=0; j<3; j++) {
719       if(N1[i]==N2[j]) {
720         sames[i] = j;
721         nbsames++;
722         break;
723       }
724     }
725   }
726   if(nbsames!=2) return false;
727   if(sames[0]>-1) {
728     shiftNodesQuadTria(N1);
729     if(sames[1]>-1) {
730       shiftNodesQuadTria(N1);
731     }
732   }
733   i = sames[0] + sames[1] + sames[2];
734   for(; i<2; i++) {
735     shiftNodesQuadTria(N2);
736   }
737   // now we receive following N1 and N2 (using numeration as in the image below)
738   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
739   // i.e. first nodes from both arrays form a new diagonal
740   return true;
741 }
742
743 //=======================================================================
744 //function : InverseDiag
745 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
746 //           but having other common link.
747 //           Return False if args are improper
748 //=======================================================================
749
750 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
751                                     const SMDS_MeshElement * theTria2 )
752 {
753   ClearLastCreated();
754
755   if ( !theTria1 || !theTria2 ||
756        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
757        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
758        theTria1->GetType() != SMDSAbs_Face ||
759        theTria2->GetType() != SMDSAbs_Face )
760     return false;
761
762   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
763       (theTria2->GetEntityType() == SMDSEntity_Triangle))
764   {
765     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
766     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
767     //    |/ |                                         | \|
768     //  B +--+ 2                                     B +--+ 2
769
770     // put nodes in array and find out indices of the same ones
771     const SMDS_MeshNode* aNodes [6];
772     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
773     int i = 0;
774     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
775     while ( it->more() ) {
776       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
777
778       if ( i > 2 ) // theTria2
779         // find same node of theTria1
780         for ( int j = 0; j < 3; j++ )
781           if ( aNodes[ i ] == aNodes[ j ]) {
782             sameInd[ j ] = i;
783             sameInd[ i ] = j;
784             break;
785           }
786       // next
787       i++;
788       if ( i == 3 ) {
789         if ( it->more() )
790           return false; // theTria1 is not a triangle
791         it = theTria2->nodesIterator();
792       }
793       if ( i == 6 && it->more() )
794         return false; // theTria2 is not a triangle
795     }
796
797     // find indices of 1,2 and of A,B in theTria1
798     int iA = -1, iB = 0, i1 = 0, i2 = 0;
799     for ( i = 0; i < 6; i++ ) {
800       if ( sameInd [ i ] == -1 ) {
801         if ( i < 3 ) i1 = i;
802         else         i2 = i;
803       }
804       else if (i < 3) {
805         if ( iA >= 0) iB = i;
806         else          iA = i;
807       }
808     }
809     // nodes 1 and 2 should not be the same
810     if ( aNodes[ i1 ] == aNodes[ i2 ] )
811       return false;
812
813     // theTria1: A->2
814     aNodes[ iA ] = aNodes[ i2 ];
815     // theTria2: B->1
816     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
817
818     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
819     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
820
821     return true;
822
823   } // end if(F1 && F2)
824
825   // check case of quadratic faces
826   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
827       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
828     return false;
829   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
830       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
831     return false;
832
833   //       5
834   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
835   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
836   //    |   / |
837   //  7 +  +  + 6
838   //    | /9  |
839   //    |/    |
840   //  4 +--+--+ 3
841   //       8
842
843   vector< const SMDS_MeshNode* > N1;
844   vector< const SMDS_MeshNode* > N2;
845   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
846     return false;
847   // now we receive following N1 and N2 (using numeration as above image)
848   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
849   // i.e. first nodes from both arrays determ new diagonal
850
851   vector< const SMDS_MeshNode*> N1new( N1.size() );
852   vector< const SMDS_MeshNode*> N2new( N2.size() );
853   N1new.back() = N1.back(); // central node of biquadratic
854   N2new.back() = N2.back();
855   N1new[0] = N1[0];  N2new[0] = N1[0];
856   N1new[1] = N2[0];  N2new[1] = N1[1];
857   N1new[2] = N2[1];  N2new[2] = N2[0];
858   N1new[3] = N1[4];  N2new[3] = N1[3];
859   N1new[4] = N2[3];  N2new[4] = N2[5];
860   N1new[5] = N1[5];  N2new[5] = N1[4];
861   // change nodes in faces
862   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
863   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
864
865   // move the central node of biquadratic triangle
866   SMESH_MesherHelper helper( *GetMesh() );
867   for ( int is2nd = 0; is2nd < 2; ++is2nd )
868   {
869     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
870     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
871     if ( nodes.size() < 7 )
872       continue;
873     helper.SetSubShape( tria->getshapeId() );
874     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
875     gp_Pnt xyz;
876     if ( F.IsNull() )
877     {
878       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
879               SMESH_NodeXYZ( nodes[4] ) +
880               SMESH_NodeXYZ( nodes[5] )) / 3.;
881     }
882     else
883     {
884       bool checkUV;
885       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
886                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
887                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
888       TopLoc_Location loc;
889       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
890       xyz = S->Value( uv.X(), uv.Y() );
891       xyz.Transform( loc );
892       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
893            nodes[6]->getshapeId() > 0 )
894         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
895     }
896     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
897   }
898   return true;
899 }
900
901 //=======================================================================
902 //function : findTriangles
903 //purpose  : find triangles sharing theNode1-theNode2 link
904 //=======================================================================
905
906 static bool findTriangles(const SMDS_MeshNode *    theNode1,
907                           const SMDS_MeshNode *    theNode2,
908                           const SMDS_MeshElement*& theTria1,
909                           const SMDS_MeshElement*& theTria2)
910 {
911   if ( !theNode1 || !theNode2 ) return false;
912
913   theTria1 = theTria2 = 0;
914
915   set< const SMDS_MeshElement* > emap;
916   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
917   while (it->more()) {
918     const SMDS_MeshElement* elem = it->next();
919     if ( elem->NbCornerNodes() == 3 )
920       emap.insert( elem );
921   }
922   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
923   while (it->more()) {
924     const SMDS_MeshElement* elem = it->next();
925     if ( emap.count( elem )) {
926       if ( !theTria1 )
927       {
928         theTria1 = elem;
929       }
930       else  
931       {
932         theTria2 = elem;
933         // theTria1 must be element with minimum ID
934         if ( theTria2->GetID() < theTria1->GetID() )
935           std::swap( theTria2, theTria1 );
936         return true;
937       }
938     }
939   }
940   return false;
941 }
942
943 //=======================================================================
944 //function : InverseDiag
945 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
946 //           with ones built on the same 4 nodes but having other common link.
947 //           Return false if proper faces not found
948 //=======================================================================
949
950 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
951                                     const SMDS_MeshNode * theNode2)
952 {
953   ClearLastCreated();
954
955   const SMDS_MeshElement *tr1, *tr2;
956   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
957     return false;
958
959   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
960        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
961     return false;
962
963   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
964       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
965
966     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
967     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
968     //    |/ |                                    | \|
969     //  B +--+ 2                                B +--+ 2
970
971     // put nodes in array
972     // and find indices of 1,2 and of A in tr1 and of B in tr2
973     int i, iA1 = 0, i1 = 0;
974     const SMDS_MeshNode* aNodes1 [3];
975     SMDS_ElemIteratorPtr it;
976     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
977       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
978       if ( aNodes1[ i ] == theNode1 )
979         iA1 = i; // node A in tr1
980       else if ( aNodes1[ i ] != theNode2 )
981         i1 = i;  // node 1
982     }
983     int iB2 = 0, i2 = 0;
984     const SMDS_MeshNode* aNodes2 [3];
985     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
986       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
987       if ( aNodes2[ i ] == theNode2 )
988         iB2 = i; // node B in tr2
989       else if ( aNodes2[ i ] != theNode1 )
990         i2 = i;  // node 2
991     }
992
993     // nodes 1 and 2 should not be the same
994     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
995       return false;
996
997     // tr1: A->2
998     aNodes1[ iA1 ] = aNodes2[ i2 ];
999     // tr2: B->1
1000     aNodes2[ iB2 ] = aNodes1[ i1 ];
1001
1002     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1003     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1004
1005     return true;
1006   }
1007
1008   // check case of quadratic faces
1009   return InverseDiag(tr1,tr2);
1010 }
1011
1012 //=======================================================================
1013 //function : getQuadrangleNodes
1014 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
1015 //           fusion of triangles tr1 and tr2 having shared link on
1016 //           theNode1 and theNode2
1017 //=======================================================================
1018
1019 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
1020                         const SMDS_MeshNode *    theNode1,
1021                         const SMDS_MeshNode *    theNode2,
1022                         const SMDS_MeshElement * tr1,
1023                         const SMDS_MeshElement * tr2 )
1024 {
1025   if( tr1->NbNodes() != tr2->NbNodes() )
1026     return false;
1027
1028   // find the 4-th node to insert into tr1
1029   const SMDS_MeshNode* n4 = 0;
1030   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1031   for ( int i = 0; !n4 && i < 3; ++i )
1032   {
1033     const SMDS_MeshNode * n = cast2Node( it->next() );
1034     bool isDiag = ( n == theNode1 || n == theNode2 );
1035     if ( !isDiag )
1036       n4 = n;
1037   }
1038
1039   // Make an array of nodes to be in a quadrangle
1040   int iNode = 0, iFirstDiag = -1;
1041   it = tr1->nodesIterator();
1042   for ( int i = 0; i < 3; ++i )
1043   {
1044     const SMDS_MeshNode * n = cast2Node( it->next() );
1045     bool isDiag = ( n == theNode1 || n == theNode2 );
1046     if ( isDiag ) {
1047       if ( iFirstDiag < 0 )
1048         iFirstDiag = iNode;
1049       else if ( iNode - iFirstDiag == 1 )
1050         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1051     }
1052     else if ( n == n4 ) {
1053       return false; // tr1 and tr2 should not have all the same nodes
1054     }
1055     theQuadNodes[ iNode++ ] = n;
1056   }
1057   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1058     theQuadNodes[ iNode ] = n4;
1059
1060   return true;
1061 }
1062
1063 //=======================================================================
1064 //function : DeleteDiag
1065 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1066 //           with a quadrangle built on the same 4 nodes.
1067 //           Return false if proper faces not found
1068 //=======================================================================
1069
1070 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1071                                    const SMDS_MeshNode * theNode2)
1072 {
1073   ClearLastCreated();
1074
1075   const SMDS_MeshElement *tr1, *tr2;
1076   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1077     return false;
1078
1079   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1080        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1081     return false;
1082
1083   SMESHDS_Mesh * aMesh = GetMeshDS();
1084
1085   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1086       (tr2->GetEntityType() == SMDSEntity_Triangle))
1087   {
1088     const SMDS_MeshNode* aNodes [ 4 ];
1089     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1090       return false;
1091
1092     const SMDS_MeshElement* newElem = 0;
1093     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1094     myLastCreatedElems.push_back(newElem);
1095     AddToSameGroups( newElem, tr1, aMesh );
1096     int aShapeId = tr1->getshapeId();
1097     if ( aShapeId )
1098       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1099
1100     aMesh->RemoveElement( tr1 );
1101     aMesh->RemoveElement( tr2 );
1102
1103     return true;
1104   }
1105
1106   // check case of quadratic faces
1107   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1108     return false;
1109   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1110     return false;
1111
1112   //       5
1113   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1114   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1115   //    |   / |
1116   //  7 +  +  + 6
1117   //    | /9  |
1118   //    |/    |
1119   //  4 +--+--+ 3
1120   //       8
1121
1122   vector< const SMDS_MeshNode* > N1;
1123   vector< const SMDS_MeshNode* > N2;
1124   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1125     return false;
1126   // now we receive following N1 and N2 (using numeration as above image)
1127   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1128   // i.e. first nodes from both arrays determ new diagonal
1129
1130   const SMDS_MeshNode* aNodes[8];
1131   aNodes[0] = N1[0];
1132   aNodes[1] = N1[1];
1133   aNodes[2] = N2[0];
1134   aNodes[3] = N2[1];
1135   aNodes[4] = N1[3];
1136   aNodes[5] = N2[5];
1137   aNodes[6] = N2[3];
1138   aNodes[7] = N1[5];
1139
1140   const SMDS_MeshElement* newElem = 0;
1141   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1142                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1143   myLastCreatedElems.push_back(newElem);
1144   AddToSameGroups( newElem, tr1, aMesh );
1145   int aShapeId = tr1->getshapeId();
1146   if ( aShapeId )
1147   {
1148     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1149   }
1150   aMesh->RemoveElement( tr1 );
1151   aMesh->RemoveElement( tr2 );
1152
1153   // remove middle node (9)
1154   GetMeshDS()->RemoveNode( N1[4] );
1155
1156   return true;
1157 }
1158
1159 //=======================================================================
1160 //function : SplitEdge
1161 //purpose  : Replace each triangle bound by theNode1-theNode2 segment with
1162 //           two triangles by connecting a node made on the link with a node opposite to the link.
1163 //=======================================================================
1164
1165 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1166                                   const SMDS_MeshNode * theNode2,
1167                                   double                thePosition)
1168 {
1169   ClearLastCreated();
1170
1171   SMESHDS_Mesh * mesh = GetMeshDS();
1172
1173   // Get triangles and segments to divide
1174
1175   std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1176   std::vector<const SMDS_MeshElement *> foundElems;
1177   if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1178     throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1179                             << theNode1->GetID() << " - " << theNode2->GetID());
1180
1181   SMESH_MesherHelper helper( *GetMesh() );
1182
1183   for ( const SMDS_MeshElement * elem : foundElems )
1184   {
1185     SMDSAbs_ElementType type = elem->GetType();
1186     switch ( type ) {
1187     case SMDSAbs_Volume:
1188       throw SALOME_Exception( "Can't split an edge of a volume");
1189       break;
1190
1191     case SMDSAbs_Face:
1192       if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1193         throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1194       if ( elem->IsQuadratic() )
1195       {
1196         helper.SetIsQuadratic( true );
1197         helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1198         helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1199       }
1200       break;
1201
1202     case SMDSAbs_Edge:
1203       if ( elem->IsQuadratic() )
1204       {
1205         helper.SetIsQuadratic( true );
1206         helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1207       }
1208       break;
1209     default:;
1210     }
1211   }
1212
1213   // Make a new node
1214
1215   const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1216
1217   gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1218                         SMESH_NodeXYZ( theNode2 ) * thePosition );
1219
1220   const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1221   if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1222   {
1223     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1224     double  tol = 100 * helper.MaxTolerance( S );
1225     gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1226     if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1227     {
1228       newNodeXYZ = surface->Value( uv );
1229       if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1230         nPos->SetParameters( uv.X(), uv.Y() );
1231     }
1232   }
1233   if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1234   {
1235     mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1236     double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1237     helper.ToFixNodeParameters( true );
1238     if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1239       newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1240   }
1241   mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1242
1243   // Split triangles and segments
1244
1245   std::vector<const SMDS_MeshNode *> nodes( 7 );
1246   for ( const SMDS_MeshElement * elem : foundElems )
1247   {
1248     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1249     nodes.resize( elem->NbCornerNodes() + 1 );
1250     nodes.back() = nodes[0];
1251
1252     smIdType id = elem->GetID();
1253     int shapeID = elem->GetShapeID();
1254
1255     const SMDS_MeshNode* centralNode = nullptr;
1256     if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1257       centralNode = elem->GetNode( 6 );
1258
1259     mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1260     if ( centralNode )
1261       mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1262
1263     for ( size_t i = 1; i < nodes.size(); ++i )
1264     {
1265       const SMDS_MeshNode* n1 = nodes[i-1];
1266       const SMDS_MeshNode* n2 = nodes[i];
1267       const SMDS_MeshElement* newElem;
1268       if ( nodes.size() == 4 ) //    triangle
1269       {
1270         bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1271         bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1272         if ( isDiag1 && isDiag2 )
1273           continue;
1274
1275         newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1276       }
1277       else //    segment
1278       {
1279         newElem = helper.AddEdge( n1, nodeOnLink, id );
1280       }
1281       myLastCreatedElems.push_back( newElem );
1282       AddToSameGroups( newElem, elem, mesh );
1283       if ( shapeID )
1284         mesh->SetMeshElementOnShape( newElem, shapeID );
1285       id = 0;
1286     }
1287   }
1288   return;
1289 }
1290
1291 //=======================================================================
1292 //function : SplitFace
1293 //purpose  : Split a face into triangles each formed by two nodes of the 
1294 //           face and a new node added at the given coordinates.
1295 //=======================================================================
1296
1297 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1298                                   double                   theX,
1299                                   double                   theY,
1300                                   double                   theZ )
1301 {
1302   ClearLastCreated();
1303
1304   if ( !theFace )
1305     throw SALOME_Exception("Null face given");
1306   if ( theFace->GetType() != SMDSAbs_Face )
1307     throw SALOME_Exception("Not a face given");
1308
1309   SMESHDS_Mesh * mesh = GetMeshDS();
1310
1311   SMESH_MesherHelper helper( *GetMesh() );
1312   if ( theFace->IsQuadratic() )
1313   {
1314     helper.SetIsQuadratic( true );
1315     helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1316   }
1317   const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1318   helper.SetSubShape( shape );
1319   helper.SetElementsOnShape( true );
1320
1321   // Make a new node
1322
1323   const SMDS_MeshNode* centralNode = nullptr;
1324   if (      theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1325     centralNode = theFace->GetNode( 6 );
1326   else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1327     centralNode = theFace->GetNode( 8 );
1328
1329   if ( centralNode )
1330   {
1331     helper.SetIsBiQuadratic( true );
1332     mesh->MoveNode( centralNode, theX, theY, theZ );
1333   }
1334   else
1335     centralNode = helper.AddNode( theX, theY, theZ );
1336
1337
1338   // Split theFace
1339
1340   std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1341   nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1342   nodes.resize( theFace->NbCornerNodes() + 1 );
1343   nodes.back() = nodes[0];
1344
1345   smIdType id = theFace->GetID();
1346   int shapeID = theFace->GetShapeID();
1347
1348   mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1349
1350   for ( size_t i = 1; i < nodes.size(); ++i )
1351   {
1352     const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1353
1354     myLastCreatedElems.push_back( newElem );
1355     AddToSameGroups( newElem, theFace, mesh );
1356     if ( shapeID )
1357       mesh->SetMeshElementOnShape( newElem, shapeID );
1358     id = 0;
1359   }
1360   return;
1361 }
1362
1363 //=======================================================================
1364 //function : Reorient
1365 //purpose  : Reverse theElement orientation
1366 //=======================================================================
1367
1368 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1369 {
1370   ClearLastCreated();
1371
1372   if (!theElem)
1373     return false;
1374   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1375   if ( !it || !it->more() )
1376     return false;
1377
1378   const SMDSAbs_ElementType type = theElem->GetType();
1379   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1380     return false;
1381
1382   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1383   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1384   {
1385     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1386     if (!aPolyedre) {
1387       MESSAGE("Warning: bad volumic element");
1388       return false;
1389     }
1390     SMDS_VolumeTool vTool( aPolyedre );
1391     const int nbFaces = vTool.NbFaces();
1392     vector<int> quantities( nbFaces );
1393     vector<const SMDS_MeshNode *> poly_nodes;
1394
1395     // check if all facets are oriented equally
1396     bool sameOri = true;
1397     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1398     for (int iface = 0; iface < nbFaces; iface++)
1399     {
1400       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1401       if ( facetOri[ iface ] != facetOri[ 0 ])
1402         sameOri = false;
1403     }
1404
1405     // reverse faces of the polyhedron
1406     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1407     poly_nodes.reserve( vTool.NbNodes() );
1408     for ( int iface = 0; iface < nbFaces; iface++ )
1409     {
1410       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1411       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1412       bool toReverse = ( facetOri[ iface ] != neededOri );
1413
1414       quantities[ iface ] = nbFaceNodes;
1415
1416       if ( toReverse )
1417         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1418           poly_nodes.push_back( nodes[ inode ]);
1419       else
1420         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1421     }
1422     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1423   }
1424   else // other elements
1425   {
1426     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1427     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1428     if ( interlace.empty() )
1429     {
1430       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1431     }
1432     else
1433     {
1434       SMDS_MeshCell::applyInterlace( interlace, nodes );
1435     }
1436     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1437   }
1438   return false;
1439 }
1440
1441 //================================================================================
1442 /*!
1443  * \brief Reorient faces.
1444  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1445  * \param theDirection - desired direction of normal of \a theRefFaces.
1446  *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1447  * \param theRefFaces - correctly oriented faces whose orientation defines
1448  *        orientation of other faces.
1449  * \return number of reoriented faces.
1450  */
1451 //================================================================================
1452
1453 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
1454                                   const gp_Vec&       theDirection,
1455                                   TIDSortedElemSet &  theRefFaces,
1456                                   bool                theAllowNonManifold )
1457 {
1458   int nbReori = 0;
1459
1460   if ( theFaces.empty() )
1461   {
1462     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1463     while ( fIt->more() )
1464       theFaces.insert( theFaces.end(), fIt->next() );
1465
1466     if ( theFaces.empty() )
1467       return nbReori;
1468   }
1469
1470   // orient theRefFaces according to theDirection
1471   if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1472     for ( const SMDS_MeshElement* refFace : theRefFaces )
1473     {
1474       gp_XYZ normal;
1475       SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1476       if ( normal * theDirection.XYZ() < 0 )
1477         nbReori += Reorient( refFace );
1478     }
1479
1480   // mark reference faces
1481   GetMeshDS()->SetAllCellsNotMarked();
1482   for ( const SMDS_MeshElement* refFace : theRefFaces )
1483     refFace->setIsMarked( true );
1484
1485   // erase reference faces from theFaces
1486   for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1487     if ( (*fIt)->isMarked() )
1488       fIt = theFaces.erase( fIt );
1489     else
1490       ++fIt;
1491
1492   if ( theRefFaces.empty() )
1493   {
1494     theRefFaces.insert( *theFaces.begin() );
1495     theFaces.erase( theFaces.begin() );
1496   }
1497
1498   // Orient theFaces
1499
1500   // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1501   //   theFaces.erase( theFace );
1502
1503   int nodeInd1, nodeInd2;
1504   const SMDS_MeshElement*           refFace, *otherFace;
1505   vector< const SMDS_MeshElement* > facesNearLink;
1506   vector< std::pair< int, int > >   nodeIndsOfFace;
1507   TIDSortedElemSet                  avoidSet, emptySet;
1508   NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1509
1510   while ( !theRefFaces.empty() )
1511   {
1512     auto refFaceIt = theRefFaces.begin();
1513     refFace = *refFaceIt;
1514     theRefFaces.erase( refFaceIt );
1515
1516     avoidSet.clear();
1517     avoidSet.insert( refFace );
1518
1519     NLink link( refFace->GetNode( 0 ), nullptr );
1520
1521     const int nbNodes = refFace->NbCornerNodes();
1522     for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1523     {
1524       link.second = refFace->GetNode(( i+1 ) % nbNodes );
1525       bool isLinkVisited = checkedLinks.Contains( link );
1526       if ( isLinkVisited )
1527       {
1528         // link has already been checked and won't be encountered more
1529         // if the group (theFaces) is manifold
1530         //checkedLinks.erase( linkIt_isNew.first );
1531       }
1532       else
1533       {
1534         checkedLinks.Add( link );
1535
1536         facesNearLink.clear();
1537         nodeIndsOfFace.clear();
1538         TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1539
1540         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1541                                                              emptySet, avoidSet,
1542                                                              &nodeInd1, &nodeInd2 )))
1543         {
1544           if (( otherFace->isMarked() ) || // ref face
1545               (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1546           {
1547             facesNearLink.push_back( otherFace );
1548             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1549           }
1550           avoidSet.insert( otherFace );
1551         }
1552         if ( facesNearLink.size() > 1 )
1553         {
1554           // NON-MANIFOLD mesh shell !
1555           if ( !theAllowNonManifold )
1556           {
1557             throw SALOME_Exception("Non-manifold topology of groups");
1558           }
1559           // select a face most co-directed with refFace,
1560           // other faces won't be visited this time
1561           gp_XYZ NF, NOF;
1562           SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1563           double proj, maxProj = -1;
1564           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1565           {
1566             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1567             if (( proj = Abs( NF * NOF )) > maxProj )
1568             {
1569               maxProj = proj;
1570               otherFace = facesNearLink[i];
1571               nodeInd1  = nodeIndsOfFace[i].first;
1572               nodeInd2  = nodeIndsOfFace[i].second;
1573             }
1574           }
1575           // not to visit rejected faces
1576           // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1577           //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1578           //     visitedFaces.insert( facesNearLink[i] );
1579         }
1580         else if ( facesNearLink.size() == 1 )
1581         {
1582           otherFace = facesNearLink[0];
1583           nodeInd1  = nodeIndsOfFace.back().first;
1584           nodeInd2  = nodeIndsOfFace.back().second;
1585         }
1586         if ( otherFace )
1587         {
1588           // link must be reverse in otherFace if orientation of otherFace
1589           // is same as that of refFace
1590           if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1591           {
1592             if ( otherFace->isMarked() )
1593               throw SALOME_Exception("Different orientation of reference faces");
1594             nbReori += Reorient( otherFace );
1595           }
1596           if ( !otherFace->isMarked() )
1597           {
1598             theRefFaces.insert( otherFace );
1599             if ( objFaceIt != theFaces.end() )
1600               theFaces.erase( objFaceIt );
1601           }
1602         }
1603       }
1604       link.first = link.second; // reverse the link
1605
1606     } // loop on links of refFace
1607
1608     if ( theRefFaces.empty() && !theFaces.empty() )
1609     {
1610       theRefFaces.insert( *theFaces.begin() );
1611       theFaces.erase( theFaces.begin() );
1612     }
1613
1614   } // while ( !theRefFaces.empty() )
1615
1616   return nbReori;
1617 }
1618
1619 //================================================================================
1620 /*!
1621  * \brief Reorient faces basing on orientation of adjacent volumes.
1622  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1623  * \param theVolumes - reference volumes.
1624  * \param theOutsideNormal - to orient faces to have their normal
1625  *        pointing either \a outside or \a inside the adjacent volumes.
1626  * \return number of reoriented faces.
1627  */
1628 //================================================================================
1629
1630 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1631                                       TIDSortedElemSet & theVolumes,
1632                                       const bool         theOutsideNormal)
1633 {
1634   int nbReori = 0;
1635
1636   SMDS_ElemIteratorPtr faceIt;
1637   if ( theFaces.empty() )
1638     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1639   else
1640     faceIt = SMESHUtils::elemSetIterator( theFaces );
1641
1642   vector< const SMDS_MeshNode* > faceNodes;
1643   TIDSortedElemSet checkedVolumes;
1644   set< const SMDS_MeshNode* > faceNodesSet;
1645   SMDS_VolumeTool volumeTool;
1646
1647   while ( faceIt->more() ) // loop on given faces
1648   {
1649     const SMDS_MeshElement* face = faceIt->next();
1650     if ( face->GetType() != SMDSAbs_Face )
1651       continue;
1652
1653     const size_t nbCornersNodes = face->NbCornerNodes();
1654     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1655
1656     checkedVolumes.clear();
1657     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1658     while ( vIt->more() )
1659     {
1660       const SMDS_MeshElement* volume = vIt->next();
1661
1662       if ( !checkedVolumes.insert( volume ).second )
1663         continue;
1664       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1665         continue;
1666
1667       // is volume adjacent?
1668       bool allNodesCommon = true;
1669       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1670         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1671       if ( !allNodesCommon )
1672         continue;
1673
1674       // get nodes of a corresponding volume facet
1675       faceNodesSet.clear();
1676       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1677       volumeTool.Set( volume );
1678       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1679       if ( facetID < 0 ) continue;
1680       volumeTool.SetExternalNormal();
1681       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1682
1683       // compare order of faceNodes and facetNodes
1684       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1685       int iNN[2];
1686       for ( int i = 0; i < 2; ++i )
1687       {
1688         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1689         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1690           if ( faceNodes[ iN ] == n )
1691           {
1692             iNN[ i ] = iN;
1693             break;
1694           }
1695       }
1696       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1697       if ( isOutside != theOutsideNormal )
1698         nbReori += Reorient( face );
1699     }
1700   }  // loop on given faces
1701
1702   return nbReori;
1703 }
1704
1705 //=======================================================================
1706 //function : getBadRate
1707 //purpose  :
1708 //=======================================================================
1709
1710 static double getBadRate (const SMDS_MeshElement*               theElem,
1711                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1712 {
1713   SMESH::Controls::TSequenceOfXYZ P;
1714   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1715     return 1e100;
1716   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1717   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1718 }
1719
1720 //=======================================================================
1721 //function : QuadToTri
1722 //purpose  : Cut quadrangles into triangles.
1723 //           theCrit is used to select a diagonal to cut
1724 //=======================================================================
1725
1726 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1727                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1728 {
1729   ClearLastCreated();
1730
1731   if ( !theCrit.get() )
1732     return false;
1733
1734   SMESHDS_Mesh *       aMesh = GetMeshDS();
1735   Handle(Geom_Surface) surface;
1736   SMESH_MesherHelper   helper( *GetMesh() );
1737
1738   myLastCreatedElems.reserve( theElems.size() * 2 );
1739
1740   TIDSortedElemSet::iterator itElem;
1741   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1742   {
1743     const SMDS_MeshElement* elem = *itElem;
1744     if ( !elem || elem->GetType() != SMDSAbs_Face )
1745       continue;
1746     if ( elem->NbCornerNodes() != 4 )
1747       continue;
1748
1749     // retrieve element nodes
1750     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1751
1752     // compare two sets of possible triangles
1753     double aBadRate1, aBadRate2; // to what extent a set is bad
1754     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1755     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1756     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1757
1758     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1759     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1760     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1761
1762     const int aShapeId = FindShape( elem );
1763     const SMDS_MeshElement* newElem1 = 0;
1764     const SMDS_MeshElement* newElem2 = 0;
1765
1766     if ( !elem->IsQuadratic() ) // split linear quadrangle
1767     {
1768       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1769       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1770       if ( aBadRate1 <= aBadRate2 ) {
1771         // tr1 + tr2 is better
1772         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1773         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1774       }
1775       else {
1776         // tr3 + tr4 is better
1777         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1778         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1779       }
1780     }
1781     else // split quadratic quadrangle
1782     {
1783       helper.SetIsQuadratic( true );
1784       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1785
1786       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1787       if ( aNodes.size() == 9 )
1788       {
1789         helper.SetIsBiQuadratic( true );
1790         if ( aBadRate1 <= aBadRate2 )
1791           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1792         else
1793           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1794       }
1795       // create a new element
1796       if ( aBadRate1 <= aBadRate2 ) {
1797         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1798         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1799       }
1800       else {
1801         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1802         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1803       }
1804     } // quadratic case
1805
1806     // care of a new element
1807
1808     myLastCreatedElems.push_back(newElem1);
1809     myLastCreatedElems.push_back(newElem2);
1810     AddToSameGroups( newElem1, elem, aMesh );
1811     AddToSameGroups( newElem2, elem, aMesh );
1812
1813     // put a new triangle on the same shape
1814     if ( aShapeId )
1815       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1816     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1817
1818     aMesh->RemoveElement( elem );
1819   }
1820   return true;
1821 }
1822
1823 //=======================================================================
1824 /*!
1825  * \brief Split each of given quadrangles into 4 triangles.
1826  * \param theElems - The faces to be split. If empty all faces are split.
1827  */
1828 //=======================================================================
1829
1830 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1831 {
1832   ClearLastCreated();
1833   myLastCreatedElems.reserve( theElems.size() * 4 );
1834
1835   SMESH_MesherHelper helper( *GetMesh() );
1836   helper.SetElementsOnShape( true );
1837
1838   // get standalone groups of faces
1839   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1840   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1841     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1842       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1843         allFaceGroups.push_back( & group->SMDSGroup() );
1844
1845   bool   checkUV;
1846   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1847   gp_XYZ xyz[9];
1848   vector< const SMDS_MeshNode* > nodes;
1849   SMESHDS_SubMesh*               subMeshDS = 0;
1850   TopoDS_Face                    F;
1851   Handle(Geom_Surface)           surface;
1852   TopLoc_Location                loc;
1853
1854   SMDS_ElemIteratorPtr faceIt;
1855   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1856   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1857
1858   while ( faceIt->more() )
1859   {
1860     const SMDS_MeshElement* quad = faceIt->next();
1861     if ( !quad || quad->NbCornerNodes() != 4 )
1862       continue;
1863
1864     // get a surface the quad is on
1865
1866     if ( quad->getshapeId() < 1 )
1867     {
1868       F.Nullify();
1869       helper.SetSubShape( 0 );
1870       subMeshDS = 0;
1871     }
1872     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1873     {
1874       helper.SetSubShape( quad->getshapeId() );
1875       if ( !helper.GetSubShape().IsNull() &&
1876            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1877       {
1878         F = TopoDS::Face( helper.GetSubShape() );
1879         surface = BRep_Tool::Surface( F, loc );
1880         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1881       }
1882       else
1883       {
1884         helper.SetSubShape( 0 );
1885         subMeshDS = 0;
1886       }
1887     }
1888
1889     // create a central node
1890
1891     const SMDS_MeshNode* nCentral;
1892     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1893
1894     if ( nodes.size() == 9 )
1895     {
1896       nCentral = nodes.back();
1897     }
1898     else
1899     {
1900       size_t iN = 0;
1901       if ( F.IsNull() )
1902       {
1903         for ( ; iN < nodes.size(); ++iN )
1904           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1905
1906         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1907           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1908
1909         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1910                                    xyz[0], xyz[1], xyz[2], xyz[3],
1911                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1912       }
1913       else
1914       {
1915         for ( ; iN < nodes.size(); ++iN )
1916           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1917
1918         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1919           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1920
1921         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1922                                   uv[0], uv[1], uv[2], uv[3],
1923                                   uv[4], uv[5], uv[6], uv[7] );
1924
1925         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1926         xyz[ 8 ] = p.XYZ();
1927       }
1928
1929       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1930                                  uv[8].X(), uv[8].Y() );
1931       myLastCreatedNodes.push_back( nCentral );
1932     }
1933
1934     helper.SetIsQuadratic  ( nodes.size() > 4 );
1935     helper.SetIsBiQuadratic( nodes.size() == 9 );
1936     if ( helper.GetIsQuadratic() )
1937       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1938
1939     // select groups to update
1940     faceGroups.clear();
1941     for ( SMDS_MeshGroup* group : allFaceGroups )
1942       if ( group->Remove( quad ))
1943         faceGroups.push_back( group );
1944
1945     // create 4 triangles
1946
1947     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1948
1949     for ( int i = 0; i < 4; ++i )
1950     {
1951       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1952                                                nodes[(i+1)%4],
1953                                                nCentral );
1954       myLastCreatedElems.push_back( tria );
1955       for ( SMDS_MeshGroup* group : faceGroups )
1956         group->Add( tria );
1957     }
1958   }
1959 }
1960
1961 //=======================================================================
1962 //function : BestSplit
1963 //purpose  : Find better diagonal for cutting.
1964 //=======================================================================
1965
1966 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1967                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1968 {
1969   ClearLastCreated();
1970
1971   if (!theCrit.get())
1972     return -1;
1973
1974   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1975     return -1;
1976
1977   if( theQuad->NbNodes()==4 ||
1978       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1979
1980     // retrieve element nodes
1981     const SMDS_MeshNode* aNodes [4];
1982     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1983     int i = 0;
1984     //while (itN->more())
1985     while (i<4) {
1986       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1987     }
1988     // compare two sets of possible triangles
1989     double aBadRate1, aBadRate2; // to what extent a set is bad
1990     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1991     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1992     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1993
1994     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1995     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1996     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1997     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1998     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1999     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2000       return 1; // diagonal 1-3
2001
2002     return 2; // diagonal 2-4
2003   }
2004   return -1;
2005 }
2006
2007 namespace
2008 {
2009   // Methods of splitting volumes into tetra
2010
2011   const int theHexTo5_1[5*4+1] =
2012     {
2013       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
2014     };
2015   const int theHexTo5_2[5*4+1] =
2016     {
2017       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
2018     };
2019   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2020
2021   const int theHexTo6_1[6*4+1] =
2022     {
2023       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
2024     };
2025   const int theHexTo6_2[6*4+1] =
2026     {
2027       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
2028     };
2029   const int theHexTo6_3[6*4+1] =
2030     {
2031       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
2032     };
2033   const int theHexTo6_4[6*4+1] =
2034     {
2035       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
2036     };
2037   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2038
2039   const int thePyraTo2_1[2*4+1] =
2040     {
2041       0, 1, 2, 4,    0, 2, 3, 4,   -1
2042     };
2043   const int thePyraTo2_2[2*4+1] =
2044     {
2045       1, 2, 3, 4,    1, 3, 0, 4,   -1
2046     };
2047   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2048
2049   const int thePentaTo3_1[3*4+1] =
2050     {
2051       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
2052     };
2053   const int thePentaTo3_2[3*4+1] =
2054     {
2055       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
2056     };
2057   const int thePentaTo3_3[3*4+1] =
2058     {
2059       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
2060     };
2061   const int thePentaTo3_4[3*4+1] =
2062     {
2063       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
2064     };
2065   const int thePentaTo3_5[3*4+1] =
2066     {
2067       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
2068     };
2069   const int thePentaTo3_6[3*4+1] =
2070     {
2071       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
2072     };
2073   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2074                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2075
2076   // Methods of splitting hexahedron into prisms
2077
2078   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2079     {
2080       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
2081     };
2082   const int theHexTo4Prisms_LR[6*4+1] = // left-right
2083     {
2084       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
2085     };
2086   const int theHexTo4Prisms_FB[6*4+1] = // front-back
2087     {
2088       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
2089     };
2090
2091   const int theHexTo2Prisms_BT_1[6*2+1] =
2092     {
2093       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
2094     };
2095   const int theHexTo2Prisms_BT_2[6*2+1] =
2096     {
2097       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
2098     };
2099   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2100
2101   const int theHexTo2Prisms_LR_1[6*2+1] =
2102     {
2103       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2104     };
2105   const int theHexTo2Prisms_LR_2[6*2+1] =
2106     {
2107       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2108     };
2109   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2110
2111   const int theHexTo2Prisms_FB_1[6*2+1] =
2112     {
2113       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
2114     };
2115   const int theHexTo2Prisms_FB_2[6*2+1] =
2116     {
2117       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
2118     };
2119   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2120
2121
2122   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2123   {
2124     int _n1, _n2, _n3;
2125     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2126     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2127     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
2128                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2129   };
2130   struct TSplitMethod
2131   {
2132     int        _nbSplits;
2133     int        _nbCorners;
2134     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2135     bool       _baryNode;     //!< additional node is to be created at cell barycenter
2136     bool       _ownConn;      //!< to delete _connectivity in destructor
2137     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2138
2139     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2140       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2141     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2142     TSplitMethod(const TSplitMethod &splitMethod)
2143       : _nbSplits(splitMethod._nbSplits),
2144         _nbCorners(splitMethod._nbCorners),
2145         _baryNode(splitMethod._baryNode),
2146         _ownConn(splitMethod._ownConn),
2147         _faceBaryNode(splitMethod._faceBaryNode)
2148     {
2149       _connectivity = splitMethod._connectivity;
2150       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2151       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2152     }
2153     bool hasFacet( const TTriangleFacet& facet ) const
2154     {
2155       if ( _nbCorners == 4 )
2156       {
2157         const int* tetConn = _connectivity;
2158         for ( ; tetConn[0] >= 0; tetConn += 4 )
2159           if (( facet.contains( tetConn[0] ) +
2160                 facet.contains( tetConn[1] ) +
2161                 facet.contains( tetConn[2] ) +
2162                 facet.contains( tetConn[3] )) == 3 )
2163             return true;
2164       }
2165       else // prism, _nbCorners == 6
2166       {
2167         const int* prismConn = _connectivity;
2168         for ( ; prismConn[0] >= 0; prismConn += 6 )
2169         {
2170           if (( facet.contains( prismConn[0] ) &&
2171                 facet.contains( prismConn[1] ) &&
2172                 facet.contains( prismConn[2] ))
2173               ||
2174               ( facet.contains( prismConn[3] ) &&
2175                 facet.contains( prismConn[4] ) &&
2176                 facet.contains( prismConn[5] )))
2177             return true;
2178         }
2179       }
2180       return false;
2181     }
2182   };
2183
2184   //=======================================================================
2185   /*!
2186    * \brief return TSplitMethod for the given element to split into tetrahedra
2187    */
2188   //=======================================================================
2189
2190   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2191   {
2192     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2193
2194     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2195     // an edge and a face barycenter; tertaherdons are based on triangles and
2196     // a volume barycenter
2197     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2198
2199     // Find out how adjacent volumes are split
2200
2201     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2202     int hasAdjacentSplits = 0, maxTetConnSize = 0;
2203     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2204     {
2205       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2206       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2207       if ( nbNodes < 4 ) continue;
2208
2209       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2210       const int* nInd = vol.GetFaceNodesIndices( iF );
2211       if ( nbNodes == 4 )
2212       {
2213         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2214         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2215         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2216         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2217       }
2218       else
2219       {
2220         int iCom = 0; // common node of triangle faces to split into
2221         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2222         {
2223           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
2224                                nInd[ iQ * ( (iCom+1)%nbNodes )],
2225                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
2226           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
2227                                nInd[ iQ * ( (iCom+2)%nbNodes )],
2228                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
2229           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2230           {
2231             triaSplits.push_back( t012 );
2232             triaSplits.push_back( t023 );
2233             break;
2234           }
2235         }
2236       }
2237       if ( !triaSplits.empty() )
2238         hasAdjacentSplits = true;
2239     }
2240
2241     // Among variants of split method select one compliant with adjacent volumes
2242
2243     TSplitMethod method;
2244     if ( !vol.Element()->IsPoly() && !is24TetMode )
2245     {
2246       int nbVariants = 2, nbTet = 0;
2247       const int** connVariants = 0;
2248       switch ( vol.Element()->GetEntityType() )
2249       {
2250       case SMDSEntity_Hexa:
2251       case SMDSEntity_Quad_Hexa:
2252       case SMDSEntity_TriQuad_Hexa:
2253         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2254           connVariants = theHexTo5, nbTet = 5;
2255         else
2256           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2257         break;
2258       case SMDSEntity_Pyramid:
2259       case SMDSEntity_Quad_Pyramid:
2260         connVariants = thePyraTo2;  nbTet = 2;
2261         break;
2262       case SMDSEntity_Penta:
2263       case SMDSEntity_Quad_Penta:
2264       case SMDSEntity_BiQuad_Penta:
2265         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2266         break;
2267       default:
2268         nbVariants = 0;
2269       }
2270       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2271       {
2272         // check method compliance with adjacent tetras,
2273         // all found splits must be among facets of tetras described by this method
2274         method = TSplitMethod( nbTet, connVariants[variant] );
2275         if ( hasAdjacentSplits && method._nbSplits > 0 )
2276         {
2277           bool facetCreated = true;
2278           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2279           {
2280             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2281             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2282               facetCreated = method.hasFacet( *facet );
2283           }
2284           if ( !facetCreated )
2285             method = TSplitMethod(0); // incompatible method
2286         }
2287       }
2288     }
2289     if ( method._nbSplits < 1 )
2290     {
2291       // No standard method is applicable, use a generic solution:
2292       // each facet of a volume is split into triangles and
2293       // each of triangles and a volume barycenter form a tetrahedron.
2294
2295       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2296
2297       int* connectivity = new int[ maxTetConnSize + 1 ];
2298       method._connectivity = connectivity;
2299       method._ownConn = true;
2300       method._baryNode = !isHex27; // to create central node or not
2301
2302       int connSize = 0;
2303       int baryCenInd = vol.NbNodes() - int( isHex27 );
2304       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2305       {
2306         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2307         const int*   nInd = vol.GetFaceNodesIndices( iF );
2308         // find common node of triangle facets of tetra to create
2309         int iCommon = 0; // index in linear numeration
2310         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2311         if ( !triaSplits.empty() )
2312         {
2313           // by found facets
2314           const TTriangleFacet* facet = &triaSplits.front();
2315           for ( ; iCommon < nbNodes-1 ; ++iCommon )
2316             if ( facet->contains( nInd[ iQ * iCommon ]) &&
2317                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2318               break;
2319         }
2320         else if ( nbNodes > 3 && !is24TetMode )
2321         {
2322           // find the best method of splitting into triangles by aspect ratio
2323           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2324           map< double, int > badness2iCommon;
2325           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2326           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2327           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2328           {
2329             double badness = 0;
2330             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2331             {
2332               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2333                                       nodes[ iQ*((iLast-1)%nbNodes)],
2334                                       nodes[ iQ*((iLast  )%nbNodes)]);
2335               badness += getBadRate( &tria, aspectRatio );
2336             }
2337             badness2iCommon.insert( make_pair( badness, iCommon ));
2338           }
2339           // use iCommon with lowest badness
2340           iCommon = badness2iCommon.begin()->second;
2341         }
2342         if ( iCommon >= nbNodes )
2343           iCommon = 0; // something wrong
2344
2345         // fill connectivity of tetrahedra based on a current face
2346         int nbTet = nbNodes - 2;
2347         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2348         {
2349           int faceBaryCenInd;
2350           if ( isHex27 )
2351           {
2352             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2353             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2354           }
2355           else
2356           {
2357             method._faceBaryNode[ iF ] = 0;
2358             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2359           }
2360           nbTet = nbNodes;
2361           for ( int i = 0; i < nbTet; ++i )
2362           {
2363             int i1 = i, i2 = (i+1) % nbNodes;
2364             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2365             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2366             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2367             connectivity[ connSize++ ] = faceBaryCenInd;
2368             connectivity[ connSize++ ] = baryCenInd;
2369           }
2370         }
2371         else
2372         {
2373           for ( int i = 0; i < nbTet; ++i )
2374           {
2375             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2376             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2377             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2378             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2379             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2380             connectivity[ connSize++ ] = baryCenInd;
2381           }
2382         }
2383         method._nbSplits += nbTet;
2384
2385       } // loop on volume faces
2386
2387       connectivity[ connSize++ ] = -1;
2388
2389     } // end of generic solution
2390
2391     return method;
2392   }
2393   //=======================================================================
2394   /*!
2395    * \brief return TSplitMethod to split haxhedron into prisms
2396    */
2397   //=======================================================================
2398
2399   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2400                                     const int        methodFlags,
2401                                     const int        facetToSplit)
2402   {
2403     TSplitMethod method;
2404
2405     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2406     // B, T, L, B, R, F
2407     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2408
2409     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2410     {
2411       static TSplitMethod to4methods[4]; // order BT, LR, FB
2412       if ( to4methods[iF]._nbSplits == 0 )
2413       {
2414         switch ( iF ) {
2415         case 0:
2416           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2417           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2418           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2419           break;
2420         case 1:
2421           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2422           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2423           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2424           break;
2425         case 2:
2426           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2427           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2428           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2429           break;
2430         default: return to4methods[3];
2431         }
2432         to4methods[iF]._nbSplits  = 4;
2433         to4methods[iF]._nbCorners = 6;
2434       }
2435       method = to4methods[iF];
2436       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2437       return method;
2438     }
2439     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2440
2441     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2442
2443     const int nbVariants = 2, nbSplits = 2;
2444     const int** connVariants = 0;
2445     switch ( iF ) {
2446     case 0: connVariants = theHexTo2Prisms_BT; break;
2447     case 1: connVariants = theHexTo2Prisms_LR; break;
2448     case 2: connVariants = theHexTo2Prisms_FB; break;
2449     default: return method;
2450     }
2451
2452     // look for prisms adjacent via facetToSplit and an opposite one
2453     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2454     {
2455       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2456       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2457       if ( nbNodes != 4 ) return method;
2458
2459       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2460       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2461       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2462       TTriangleFacet* t;
2463       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2464         t = &t012;
2465       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2466         t = &t123;
2467       else
2468         continue;
2469
2470       // there are adjacent prism
2471       for ( int variant = 0; variant < nbVariants; ++variant )
2472       {
2473         // check method compliance with adjacent prisms,
2474         // the found prism facets must be among facets of prisms described by current method
2475         method._nbSplits     = nbSplits;
2476         method._nbCorners    = 6;
2477         method._connectivity = connVariants[ variant ];
2478         if ( method.hasFacet( *t ))
2479           return method;
2480       }
2481     }
2482
2483     // No adjacent prisms. Select a variant with a best aspect ratio.
2484
2485     double badness[2] = { 0., 0. };
2486     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2487     const SMDS_MeshNode** nodes = vol.GetNodes();
2488     for ( int variant = 0; variant < nbVariants; ++variant )
2489       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2490       {
2491         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2492         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2493
2494         method._connectivity = connVariants[ variant ];
2495         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2496         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2497         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2498
2499         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2500                                 nodes[ t->_n2 ],
2501                                 nodes[ t->_n3 ] );
2502         badness[ variant ] += getBadRate( &tria, aspectRatio );
2503       }
2504     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2505
2506     method._nbSplits     = nbSplits;
2507     method._nbCorners    = 6;
2508     method._connectivity = connVariants[ iBetter ];
2509
2510     return method;
2511   }
2512
2513   //================================================================================
2514   /*!
2515    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2516    */
2517   //================================================================================
2518
2519   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2520                                        const SMDSAbs_GeometryType geom ) const
2521   {
2522     // find the tetrahedron including the three nodes of facet
2523     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2524     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2525     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2526     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2527     while ( volIt1->more() )
2528     {
2529       const SMDS_MeshElement* v = volIt1->next();
2530       if ( v->GetGeomType() != geom )
2531         continue;
2532       const int lastCornerInd = v->NbCornerNodes() - 1;
2533       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2534         continue; // medium node not allowed
2535       const int ind2 = v->GetNodeIndex( n2 );
2536       if ( ind2 < 0 || lastCornerInd < ind2 )
2537         continue;
2538       const int ind3 = v->GetNodeIndex( n3 );
2539       if ( ind3 < 0 || lastCornerInd < ind3 )
2540         continue;
2541       return true;
2542     }
2543     return false;
2544   }
2545
2546   //=======================================================================
2547   /*!
2548    * \brief A key of a face of volume
2549    */
2550   //=======================================================================
2551
2552   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2553   {
2554     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2555     {
2556       TIDSortedNodeSet sortedNodes;
2557       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2558       int nbNodes = vol.NbFaceNodes( iF );
2559       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2560       for ( int i = 0; i < nbNodes; i += iQ )
2561         sortedNodes.insert( fNodes[i] );
2562       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2563       first.first   = (*(n++))->GetID();
2564       first.second  = (*(n++))->GetID();
2565       second.first  = (*(n++))->GetID();
2566       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2567     }
2568   };
2569 } // namespace
2570
2571 //=======================================================================
2572 //function : SplitVolumes
2573 //purpose  : Split volume elements into tetrahedra or prisms.
2574 //           If facet ID < 0, element is split into tetrahedra,
2575 //           else a hexahedron is split into prisms so that the given facet is
2576 //           split into triangles
2577 //=======================================================================
2578
2579 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2580                                      const int            theMethodFlags)
2581 {
2582   SMDS_VolumeTool    volTool;
2583   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2584   fHelper.ToFixNodeParameters( true );
2585
2586   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2587   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2588
2589   SMESH_SequenceOfElemPtr newNodes, newElems;
2590
2591   // map face of volume to it's baricenrtic node
2592   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2593   double bc[3];
2594   vector<const SMDS_MeshElement* > splitVols;
2595
2596   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2597   for ( ; elem2facet != theElems.end(); ++elem2facet )
2598   {
2599     const SMDS_MeshElement* elem = elem2facet->first;
2600     const int       facetToSplit = elem2facet->second;
2601     if ( elem->GetType() != SMDSAbs_Volume )
2602       continue;
2603     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2604     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2605       continue;
2606
2607     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2608
2609     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2610                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2611                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2612     if ( splitMethod._nbSplits < 1 ) continue;
2613
2614     // find submesh to add new tetras to
2615     if ( !subMesh || !subMesh->Contains( elem ))
2616     {
2617       int shapeID = FindShape( elem );
2618       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2619       subMesh = GetMeshDS()->MeshElements( shapeID );
2620     }
2621     int iQ;
2622     if ( elem->IsQuadratic() )
2623     {
2624       iQ = 2;
2625       // add quadratic links to the helper
2626       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2627       {
2628         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2629         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2630         for ( int iN = 0; iN < nbN; iN += iQ )
2631           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2632       }
2633       helper.SetIsQuadratic( true );
2634     }
2635     else
2636     {
2637       iQ = 1;
2638       helper.SetIsQuadratic( false );
2639     }
2640     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2641                                         volTool.GetNodes() + elem->NbNodes() );
2642     helper.SetElementsOnShape( true );
2643     if ( splitMethod._baryNode )
2644     {
2645       // make a node at barycenter
2646       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2647       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2648       nodes.push_back( gcNode );
2649       newNodes.push_back( gcNode );
2650     }
2651     if ( !splitMethod._faceBaryNode.empty() )
2652     {
2653       // make or find baricentric nodes of faces
2654       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2655       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2656       {
2657         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2658           volFace2BaryNode.insert
2659           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2660         if ( !f_n->second )
2661         {
2662           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2663           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2664         }
2665         nodes.push_back( iF_n->second = f_n->second );
2666       }
2667     }
2668
2669     // make new volumes
2670     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2671     const int* volConn = splitMethod._connectivity;
2672     if ( splitMethod._nbCorners == 4 ) // tetra
2673       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2674         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2675                                                                nodes[ volConn[1] ],
2676                                                                nodes[ volConn[2] ],
2677                                                                nodes[ volConn[3] ]));
2678     else // prisms
2679       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2680         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2681                                                                nodes[ volConn[1] ],
2682                                                                nodes[ volConn[2] ],
2683                                                                nodes[ volConn[3] ],
2684                                                                nodes[ volConn[4] ],
2685                                                                nodes[ volConn[5] ]));
2686
2687     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2688
2689     // Split faces on sides of the split volume
2690
2691     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2692     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2693     {
2694       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2695       if ( nbNodes < 4 ) continue;
2696
2697       // find an existing face
2698       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2699                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2700       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2701                                                                        /*noMedium=*/false))
2702       {
2703         // make triangles
2704         helper.SetElementsOnShape( false );
2705         vector< const SMDS_MeshElement* > triangles;
2706
2707         // find submesh to add new triangles in
2708         if ( !fSubMesh || !fSubMesh->Contains( face ))
2709         {
2710           int shapeID = FindShape( face );
2711           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2712         }
2713         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2714         if ( iF_n != splitMethod._faceBaryNode.end() )
2715         {
2716           const SMDS_MeshNode *baryNode = iF_n->second;
2717           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2718           {
2719             const SMDS_MeshNode* n1 = fNodes[iN];
2720             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2721             const SMDS_MeshNode *n3 = baryNode;
2722             if ( !volTool.IsFaceExternal( iF ))
2723               swap( n2, n3 );
2724             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2725           }
2726           if ( fSubMesh ) // update position of the bary node on geometry
2727           {
2728             if ( subMesh )
2729               subMesh->RemoveNode( baryNode );
2730             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2731             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2732             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2733             {
2734               fHelper.SetSubShape( s );
2735               gp_XY uv( 1e100, 1e100 );
2736               double distXYZ[4];
2737               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2738                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2739                    uv.X() < 1e100 )
2740               {
2741                 // node is too far from the surface
2742                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2743                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2744                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2745               }
2746             }
2747           }
2748         }
2749         else
2750         {
2751           // among possible triangles create ones described by split method
2752           const int* nInd = volTool.GetFaceNodesIndices( iF );
2753           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2754           int iCom = 0; // common node of triangle faces to split into
2755           list< TTriangleFacet > facets;
2756           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2757           {
2758             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2759                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2760                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2761             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2762                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2763                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2764             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2765             {
2766               facets.push_back( t012 );
2767               facets.push_back( t023 );
2768               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2769                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2770                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2771                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2772               break;
2773             }
2774           }
2775           list< TTriangleFacet >::iterator facet = facets.begin();
2776           if ( facet == facets.end() )
2777             break;
2778           for ( ; facet != facets.end(); ++facet )
2779           {
2780             if ( !volTool.IsFaceExternal( iF ))
2781               swap( facet->_n2, facet->_n3 );
2782             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2783                                                  volNodes[ facet->_n2 ],
2784                                                  volNodes[ facet->_n3 ]));
2785           }
2786         }
2787         for ( size_t i = 0; i < triangles.size(); ++i )
2788         {
2789           if ( !triangles[ i ]) continue;
2790           if ( fSubMesh )
2791             fSubMesh->AddElement( triangles[ i ]);
2792           newElems.push_back( triangles[ i ]);
2793         }
2794         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2795         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2796
2797       } // while a face based on facet nodes exists
2798     } // loop on volume faces to split them into triangles
2799
2800     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2801
2802     if ( geomType == SMDSEntity_TriQuad_Hexa )
2803     {
2804       // remove medium nodes that could become free
2805       for ( int i = 20; i < volTool.NbNodes(); ++i )
2806         if ( volNodes[i]->NbInverseElements() == 0 )
2807           GetMeshDS()->RemoveNode( volNodes[i] );
2808     }
2809   } // loop on volumes to split
2810
2811   myLastCreatedNodes = newNodes;
2812   myLastCreatedElems = newElems;
2813 }
2814
2815 //=======================================================================
2816 //function : GetHexaFacetsToSplit
2817 //purpose  : For hexahedra that will be split into prisms, finds facets to
2818 //           split into triangles. Only hexahedra adjacent to the one closest
2819 //           to theFacetNormal.Location() are returned.
2820 //param [in,out] theHexas - the hexahedra
2821 //param [in]     theFacetNormal - facet normal
2822 //param [out]    theFacets - the hexahedra and found facet IDs
2823 //=======================================================================
2824
2825 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2826                                              const gp_Ax1&     theFacetNormal,
2827                                              TFacetOfElem &    theFacets)
2828 {
2829 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2830
2831   // Find a hexa closest to the location of theFacetNormal
2832
2833   const SMDS_MeshElement* startHex;
2834   {
2835     // get SMDS_ElemIteratorPtr on theHexas
2836     typedef const SMDS_MeshElement*                                      TValue;
2837     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2838     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2839     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2840     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2841     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2842       ( new TElemSetIter( theHexas.begin(),
2843                           theHexas.end(),
2844                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2845
2846     SMESH_ElementSearcher* searcher =
2847       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2848
2849     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2850
2851     delete searcher;
2852
2853     if ( !startHex )
2854       throw SALOME_Exception( THIS_METHOD "startHex not found");
2855   }
2856
2857   // Select a facet of startHex by theFacetNormal
2858
2859   SMDS_VolumeTool vTool( startHex );
2860   double norm[3], dot, maxDot = 0;
2861   int facetID = -1;
2862   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2863     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2864     {
2865       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2866       if ( dot > maxDot )
2867       {
2868         facetID = iF;
2869         maxDot = dot;
2870       }
2871     }
2872   if ( facetID < 0 )
2873     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2874
2875   // Fill theFacets starting from facetID of startHex
2876
2877   // facets used for searching of volumes adjacent to already treated ones
2878   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2879   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2880   TFacetMap facetsToCheck;
2881
2882   set<const SMDS_MeshNode*> facetNodes;
2883   const SMDS_MeshElement*   curHex;
2884
2885   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2886
2887   while ( startHex )
2888   {
2889     // move in two directions from startHex via facetID
2890     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2891     {
2892       curHex       = startHex;
2893       int curFacet = facetID;
2894       if ( is2nd ) // do not treat startHex twice
2895       {
2896         vTool.Set( curHex );
2897         if ( vTool.IsFreeFace( curFacet, &curHex ))
2898         {
2899           curHex = 0;
2900         }
2901         else
2902         {
2903           vTool.GetFaceNodes( curFacet, facetNodes );
2904           vTool.Set( curHex );
2905           curFacet = vTool.GetFaceIndex( facetNodes );
2906         }
2907       }
2908       while ( curHex )
2909       {
2910         // store a facet to split
2911         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2912         {
2913           theFacets.insert( make_pair( curHex, -1 ));
2914           break;
2915         }
2916         if ( !allHex && !theHexas.count( curHex ))
2917           break;
2918
2919         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2920           theFacets.insert( make_pair( curHex, curFacet ));
2921         if ( !facetIt2isNew.second )
2922           break;
2923
2924         // remember not-to-split facets in facetsToCheck
2925         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2926         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2927         {
2928           if ( iF == curFacet && iF == oppFacet )
2929             continue;
2930           TVolumeFaceKey facetKey ( vTool, iF );
2931           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2932           pair< TFacetMap::iterator, bool > it2isnew =
2933             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2934           if ( !it2isnew.second )
2935             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2936         }
2937         // pass to a volume adjacent via oppFacet
2938         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2939         {
2940           curHex = 0;
2941         }
2942         else
2943         {
2944           // get a new curFacet
2945           vTool.GetFaceNodes( oppFacet, facetNodes );
2946           vTool.Set( curHex );
2947           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2948         }
2949       }
2950     } // move in two directions from startHex via facetID
2951
2952     // Find a new startHex by facetsToCheck
2953
2954     startHex = 0;
2955     facetID  = -1;
2956     TFacetMap::iterator fIt = facetsToCheck.begin();
2957     while ( !startHex && fIt != facetsToCheck.end() )
2958     {
2959       const TElemFacets&  elemFacets = fIt->second;
2960       const SMDS_MeshElement*    hex = elemFacets.first->first;
2961       int                 splitFacet = elemFacets.first->second;
2962       int               lateralFacet = elemFacets.second;
2963       facetsToCheck.erase( fIt );
2964       fIt = facetsToCheck.begin();
2965
2966       vTool.Set( hex );
2967       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2968            curHex->GetGeomType() != SMDSGeom_HEXA )
2969         continue;
2970       if ( !allHex && !theHexas.count( curHex ))
2971         continue;
2972
2973       startHex = curHex;
2974
2975       // find a facet of startHex to split
2976
2977       set<const SMDS_MeshNode*> lateralNodes;
2978       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2979       vTool.GetFaceNodes( splitFacet,   facetNodes );
2980       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2981       vTool.Set( startHex );
2982       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2983
2984       // look for a facet of startHex having common nodes with facetNodes
2985       // but not lateralFacet
2986       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2987       {
2988         if ( iF == lateralFacet )
2989           continue;
2990         int nbCommonNodes = 0;
2991         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2992         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2993           nbCommonNodes += facetNodes.count( nn[ iN ]);
2994
2995         if ( nbCommonNodes >= 2 )
2996         {
2997           facetID = iF;
2998           break;
2999         }
3000       }
3001       if ( facetID < 0 )
3002         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3003     }
3004   } //   while ( startHex )
3005
3006   return;
3007 }
3008
3009 namespace
3010 {
3011   //================================================================================
3012   /*!
3013    * \brief Selects nodes of several elements according to a given interlace
3014    *  \param [in] srcNodes - nodes to select from
3015    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
3016    *  \param [in] interlace - indices of nodes for all elements
3017    *  \param [in] nbElems - nb of elements
3018    *  \param [in] nbNodes - nb of nodes in each element
3019    *  \param [in] mesh - the mesh
3020    *  \param [out] elemQueue - a list to push elements found by the selected nodes
3021    *  \param [in] type - type of elements to look for
3022    */
3023   //================================================================================
3024
3025   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3026                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
3027                     const int*                            interlace,
3028                     const int                             nbElems,
3029                     const int                             nbNodes,
3030                     SMESHDS_Mesh*                         mesh = 0,
3031                     list< const SMDS_MeshElement* >*      elemQueue=0,
3032                     SMDSAbs_ElementType                   type=SMDSAbs_All)
3033   {
3034     for ( int iE = 0; iE < nbElems; ++iE )
3035     {
3036       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3037       const int*                         select = & interlace[iE*nbNodes];
3038       elemNodes.resize( nbNodes );
3039       for ( int iN = 0; iN < nbNodes; ++iN )
3040         elemNodes[iN] = srcNodes[ select[ iN ]];
3041     }
3042     const SMDS_MeshElement* e;
3043     if ( elemQueue )
3044       for ( int iE = 0; iE < nbElems; ++iE )
3045         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3046           elemQueue->push_back( e );
3047   }
3048 }
3049
3050 //=======================================================================
3051 /*
3052  * Split bi-quadratic elements into linear ones without creation of additional nodes
3053  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
3054  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3055  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3056  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
3057  *   will be split in order to keep the mesh conformal.
3058  *  \param elems - elements to split
3059  */
3060 //=======================================================================
3061
3062 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3063 {
3064   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3065   vector<const SMDS_MeshElement* > splitElems;
3066   list< const SMDS_MeshElement* > elemQueue;
3067   list< const SMDS_MeshElement* >::iterator elemIt;
3068
3069   SMESHDS_Mesh * mesh = GetMeshDS();
3070   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3071   int nbElems, nbNodes;
3072
3073   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3074   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3075   {
3076     elemQueue.clear();
3077     elemQueue.push_back( *elemSetIt );
3078     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3079     {
3080       const SMDS_MeshElement* elem = *elemIt;
3081       switch( elem->GetEntityType() )
3082       {
3083       case SMDSEntity_TriQuad_Hexa: // HEX27
3084       {
3085         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3086         nbElems  = nbNodes = 8;
3087         elemType = & hexaType;
3088
3089         // get nodes for new elements
3090         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
3091                                  { 1,9,20,8,    17,22,26,21 },
3092                                  { 2,10,20,9,   18,23,26,22 },
3093                                  { 3,11,20,10,  19,24,26,23 },
3094                                  { 16,21,26,24, 4,12,25,15  },
3095                                  { 17,22,26,21, 5,13,25,12  },
3096                                  { 18,23,26,22, 6,14,25,13  },
3097                                  { 19,24,26,23, 7,15,25,14  }};
3098         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3099
3100         // add boundary faces to elemQueue
3101         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
3102                                  { 4,5,6,7, 12,13,14,15, 25 },
3103                                  { 0,1,5,4, 8,17,12,16,  21 },
3104                                  { 1,2,6,5, 9,18,13,17,  22 },
3105                                  { 2,3,7,6, 10,19,14,18, 23 },
3106                                  { 3,0,4,7, 11,16,15,19, 24 }};
3107         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3108
3109         // add boundary segments to elemQueue
3110         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3111                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3112                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3113         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3114         break;
3115       }
3116       case SMDSEntity_BiQuad_Triangle: // TRIA7
3117       {
3118         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3119         nbElems = 3;
3120         nbNodes = 4;
3121         elemType = & quadType;
3122
3123         // get nodes for new elements
3124         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3125         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3126
3127         // add boundary segments to elemQueue
3128         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3129         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3130         break;
3131       }
3132       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3133       {
3134         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3135         nbElems = 4;
3136         nbNodes = 4;
3137         elemType = & quadType;
3138
3139         // get nodes for new elements
3140         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3141         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3142
3143         // add boundary segments to elemQueue
3144         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3145         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3146         break;
3147       }
3148       case SMDSEntity_Quad_Edge:
3149       {
3150         if ( elemIt == elemQueue.begin() )
3151           continue; // an elem is in theElems
3152         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3153         nbElems = 2;
3154         nbNodes = 2;
3155         elemType = & segType;
3156
3157         // get nodes for new elements
3158         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3159         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3160         break;
3161       }
3162       default: continue;
3163       } // switch( elem->GetEntityType() )
3164
3165       // Create new elements
3166
3167       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3168
3169       splitElems.clear();
3170
3171       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3172       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3173       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3174       //elemType->SetID( -1 );
3175
3176       for ( int iE = 0; iE < nbElems; ++iE )
3177         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3178
3179
3180       ReplaceElemInGroups( elem, splitElems, mesh );
3181
3182       if ( subMesh )
3183         for ( size_t i = 0; i < splitElems.size(); ++i )
3184           subMesh->AddElement( splitElems[i] );
3185     }
3186   }
3187 }
3188
3189 //=======================================================================
3190 //function : AddToSameGroups
3191 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
3192 //=======================================================================
3193
3194 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3195                                         const SMDS_MeshElement* elemInGroups,
3196                                         SMESHDS_Mesh *          aMesh)
3197 {
3198   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3199   if (!groups.empty()) {
3200     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3201     for ( ; grIt != groups.end(); grIt++ ) {
3202       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3203       if ( group && group->Contains( elemInGroups ))
3204         group->SMDSGroup().Add( elemToAdd );
3205     }
3206   }
3207 }
3208
3209
3210 //=======================================================================
3211 //function : RemoveElemFromGroups
3212 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
3213 //=======================================================================
3214 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3215                                              SMESHDS_Mesh *          aMesh)
3216 {
3217   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3218   if (!groups.empty())
3219   {
3220     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3221     for (; GrIt != groups.end(); GrIt++)
3222     {
3223       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3224       if (!grp || grp->IsEmpty()) continue;
3225       grp->SMDSGroup().Remove(removeelem);
3226     }
3227   }
3228 }
3229
3230 //================================================================================
3231 /*!
3232  * \brief Replace elemToRm by elemToAdd in the all groups
3233  */
3234 //================================================================================
3235
3236 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3237                                             const SMDS_MeshElement* elemToAdd,
3238                                             SMESHDS_Mesh *          aMesh)
3239 {
3240   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3241   if (!groups.empty()) {
3242     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3243     for ( ; grIt != groups.end(); grIt++ ) {
3244       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3245       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3246         group->SMDSGroup().Add( elemToAdd );
3247     }
3248   }
3249 }
3250
3251 //================================================================================
3252 /*!
3253  * \brief Replace elemToRm by elemToAdd in the all groups
3254  */
3255 //================================================================================
3256
3257 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
3258                                             const vector<const SMDS_MeshElement*>& elemToAdd,
3259                                             SMESHDS_Mesh *                         aMesh)
3260 {
3261   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3262   if (!groups.empty())
3263   {
3264     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3265     for ( ; grIt != groups.end(); grIt++ ) {
3266       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3267       if ( group && group->SMDSGroup().Remove( elemToRm ) )
3268         for ( size_t i = 0; i < elemToAdd.size(); ++i )
3269           group->SMDSGroup().Add( elemToAdd[ i ] );
3270     }
3271   }
3272 }
3273
3274 //=======================================================================
3275 //function : QuadToTri
3276 //purpose  : Cut quadrangles into triangles.
3277 //           theCrit is used to select a diagonal to cut
3278 //=======================================================================
3279
3280 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3281                                   const bool         the13Diag)
3282 {
3283   ClearLastCreated();
3284   myLastCreatedElems.reserve( theElems.size() * 2 );
3285
3286   SMESHDS_Mesh *       aMesh = GetMeshDS();
3287   Handle(Geom_Surface) surface;
3288   SMESH_MesherHelper   helper( *GetMesh() );
3289
3290   TIDSortedElemSet::iterator itElem;
3291   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3292   {
3293     const SMDS_MeshElement* elem = *itElem;
3294     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3295       continue;
3296
3297     if ( elem->NbNodes() == 4 ) {
3298       // retrieve element nodes
3299       const SMDS_MeshNode* aNodes [4];
3300       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3301       int i = 0;
3302       while ( itN->more() )
3303         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3304
3305       int aShapeId = FindShape( elem );
3306       const SMDS_MeshElement* newElem1 = 0;
3307       const SMDS_MeshElement* newElem2 = 0;
3308       if ( the13Diag ) {
3309         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3310         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3311       }
3312       else {
3313         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3314         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3315       }
3316       myLastCreatedElems.push_back(newElem1);
3317       myLastCreatedElems.push_back(newElem2);
3318       // put a new triangle on the same shape and add to the same groups
3319       if ( aShapeId )
3320       {
3321         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3322         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3323       }
3324       AddToSameGroups( newElem1, elem, aMesh );
3325       AddToSameGroups( newElem2, elem, aMesh );
3326       aMesh->RemoveElement( elem );
3327     }
3328
3329     // Quadratic quadrangle
3330
3331     else if ( elem->NbNodes() >= 8 )
3332     {
3333       // get surface elem is on
3334       int aShapeId = FindShape( elem );
3335       if ( aShapeId != helper.GetSubShapeID() ) {
3336         surface.Nullify();
3337         TopoDS_Shape shape;
3338         if ( aShapeId > 0 )
3339           shape = aMesh->IndexToShape( aShapeId );
3340         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3341           TopoDS_Face face = TopoDS::Face( shape );
3342           surface = BRep_Tool::Surface( face );
3343           if ( !surface.IsNull() )
3344             helper.SetSubShape( shape );
3345         }
3346       }
3347
3348       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3349       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3350       for ( int i = 0; itN->more(); ++i )
3351         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3352
3353       const SMDS_MeshNode* centrNode = aNodes[8];
3354       if ( centrNode == 0 )
3355       {
3356         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3358                                            surface.IsNull() );
3359         myLastCreatedNodes.push_back(centrNode);
3360       }
3361
3362       // create a new element
3363       const SMDS_MeshElement* newElem1 = 0;
3364       const SMDS_MeshElement* newElem2 = 0;
3365       if ( the13Diag ) {
3366         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3367                                   aNodes[6], aNodes[7], centrNode );
3368         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3369                                   centrNode, aNodes[4], aNodes[5] );
3370       }
3371       else {
3372         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3373                                   aNodes[7], aNodes[4], centrNode );
3374         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3375                                   centrNode, aNodes[5], aNodes[6] );
3376       }
3377       myLastCreatedElems.push_back(newElem1);
3378       myLastCreatedElems.push_back(newElem2);
3379       // put a new triangle on the same shape and add to the same groups
3380       if ( aShapeId )
3381       {
3382         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3383         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3384       }
3385       AddToSameGroups( newElem1, elem, aMesh );
3386       AddToSameGroups( newElem2, elem, aMesh );
3387       aMesh->RemoveElement( elem );
3388     }
3389   }
3390
3391   return true;
3392 }
3393
3394 //=======================================================================
3395 //function : getAngle
3396 //purpose  :
3397 //=======================================================================
3398
3399 double getAngle(const SMDS_MeshElement * tr1,
3400                 const SMDS_MeshElement * tr2,
3401                 const SMDS_MeshNode *    n1,
3402                 const SMDS_MeshNode *    n2)
3403 {
3404   double angle = 2. * M_PI; // bad angle
3405
3406   // get normals
3407   SMESH::Controls::TSequenceOfXYZ P1, P2;
3408   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3409        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3410     return angle;
3411   gp_Vec N1,N2;
3412   if(!tr1->IsQuadratic())
3413     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3414   else
3415     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3416   if ( N1.SquareMagnitude() <= gp::Resolution() )
3417     return angle;
3418   if(!tr2->IsQuadratic())
3419     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3420   else
3421     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3422   if ( N2.SquareMagnitude() <= gp::Resolution() )
3423     return angle;
3424
3425   // find the first diagonal node n1 in the triangles:
3426   // take in account a diagonal link orientation
3427   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3428   for ( int t = 0; t < 2; t++ ) {
3429     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3430     int i = 0, iDiag = -1;
3431     while ( it->more()) {
3432       const SMDS_MeshElement *n = it->next();
3433       if ( n == n1 || n == n2 ) {
3434         if ( iDiag < 0)
3435           iDiag = i;
3436         else {
3437           if ( i - iDiag == 1 )
3438             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3439           else
3440             nFirst[ t ] = n;
3441           break;
3442         }
3443       }
3444       i++;
3445     }
3446   }
3447   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3448     N2.Reverse();
3449
3450   angle = N1.Angle( N2 );
3451   //SCRUTE( angle );
3452   return angle;
3453 }
3454
3455 // =================================================
3456 // class generating a unique ID for a pair of nodes
3457 // and able to return nodes by that ID
3458 // =================================================
3459 class LinkID_Gen {
3460 public:
3461
3462   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3463     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3464   {}
3465
3466   smIdType GetLinkID (const SMDS_MeshNode * n1,
3467                   const SMDS_MeshNode * n2) const
3468   {
3469     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3470   }
3471
3472   bool GetNodes (const long             theLinkID,
3473                  const SMDS_MeshNode* & theNode1,
3474                  const SMDS_MeshNode* & theNode2) const
3475   {
3476     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3477     if ( !theNode1 ) return false;
3478     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3479     if ( !theNode2 ) return false;
3480     return true;
3481   }
3482
3483 private:
3484   LinkID_Gen();
3485   const SMESHDS_Mesh* myMesh;
3486   long                myMaxID;
3487 };
3488
3489
3490 //=======================================================================
3491 //function : TriToQuad
3492 //purpose  : Fuse neighbour triangles into quadrangles.
3493 //           theCrit is used to select a neighbour to fuse with.
3494 //           theMaxAngle is a max angle between element normals at which
3495 //           fusion is still performed.
3496 //=======================================================================
3497
3498 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3499                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3500                                   const double                         theMaxAngle)
3501 {
3502   ClearLastCreated();
3503   myLastCreatedElems.reserve( theElems.size() / 2 );
3504
3505   if ( !theCrit.get() )
3506     return false;
3507
3508   SMESHDS_Mesh * aMesh = GetMeshDS();
3509
3510   // Prepare data for algo: build
3511   // 1. map of elements with their linkIDs
3512   // 2. map of linkIDs with their elements
3513
3514   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3515   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3516   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3517   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3518
3519   TIDSortedElemSet::iterator itElem;
3520   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3521   {
3522     const SMDS_MeshElement* elem = *itElem;
3523     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3524     bool IsTria = ( elem->NbCornerNodes()==3 );
3525     if (!IsTria) continue;
3526
3527     // retrieve element nodes
3528     const SMDS_MeshNode* aNodes [4];
3529     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3530     int i = 0;
3531     while ( i < 3 )
3532       aNodes[ i++ ] = itN->next();
3533     aNodes[ 3 ] = aNodes[ 0 ];
3534
3535     // fill maps
3536     for ( i = 0; i < 3; i++ ) {
3537       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3538       // check if elements sharing a link can be fused
3539       itLE = mapLi_listEl.find( link );
3540       if ( itLE != mapLi_listEl.end() ) {
3541         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3542           continue;
3543         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3544         //if ( FindShape( elem ) != FindShape( elem2 ))
3545         //  continue; // do not fuse triangles laying on different shapes
3546         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3547           continue; // avoid making badly shaped quads
3548         (*itLE).second.push_back( elem );
3549       }
3550       else {
3551         mapLi_listEl[ link ].push_back( elem );
3552       }
3553       mapEl_setLi [ elem ].insert( link );
3554     }
3555   }
3556   // Clean the maps from the links shared by a sole element, ie
3557   // links to which only one element is bound in mapLi_listEl
3558
3559   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3560     int nbElems = (*itLE).second.size();
3561     if ( nbElems < 2  ) {
3562       const SMDS_MeshElement* elem = (*itLE).second.front();
3563       SMESH_TLink link = (*itLE).first;
3564       mapEl_setLi[ elem ].erase( link );
3565       if ( mapEl_setLi[ elem ].empty() )
3566         mapEl_setLi.erase( elem );
3567     }
3568   }
3569
3570   // Algo: fuse triangles into quadrangles
3571
3572   while ( ! mapEl_setLi.empty() ) {
3573     // Look for the start element:
3574     // the element having the least nb of shared links
3575     const SMDS_MeshElement* startElem = 0;
3576     int minNbLinks = 4;
3577     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3578       int nbLinks = (*itEL).second.size();
3579       if ( nbLinks < minNbLinks ) {
3580         startElem = (*itEL).first;
3581         minNbLinks = nbLinks;
3582         if ( minNbLinks == 1 )
3583           break;
3584       }
3585     }
3586
3587     // search elements to fuse starting from startElem or links of elements
3588     // fused earlyer - startLinks
3589     list< SMESH_TLink > startLinks;
3590     while ( startElem || !startLinks.empty() ) {
3591       while ( !startElem && !startLinks.empty() ) {
3592         // Get an element to start, by a link
3593         SMESH_TLink linkId = startLinks.front();
3594         startLinks.pop_front();
3595         itLE = mapLi_listEl.find( linkId );
3596         if ( itLE != mapLi_listEl.end() ) {
3597           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3598           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3599           for ( ; itE != listElem.end() ; itE++ )
3600             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3601               startElem = (*itE);
3602           mapLi_listEl.erase( itLE );
3603         }
3604       }
3605
3606       if ( startElem ) {
3607         // Get candidates to be fused
3608         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3609         const SMESH_TLink *link12 = 0, *link13 = 0;
3610         startElem = 0;
3611         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3612         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3613         ASSERT( !setLi.empty() );
3614         set< SMESH_TLink >::iterator itLi;
3615         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3616         {
3617           const SMESH_TLink & link = (*itLi);
3618           itLE = mapLi_listEl.find( link );
3619           if ( itLE == mapLi_listEl.end() )
3620             continue;
3621
3622           const SMDS_MeshElement* elem = (*itLE).second.front();
3623           if ( elem == tr1 )
3624             elem = (*itLE).second.back();
3625           mapLi_listEl.erase( itLE );
3626           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3627             continue;
3628           if ( tr2 ) {
3629             tr3 = elem;
3630             link13 = &link;
3631           }
3632           else {
3633             tr2 = elem;
3634             link12 = &link;
3635           }
3636
3637           // add other links of elem to list of links to re-start from
3638           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3639           set< SMESH_TLink >::iterator it;
3640           for ( it = links.begin(); it != links.end(); it++ ) {
3641             const SMESH_TLink& link2 = (*it);
3642             if ( link2 != link )
3643               startLinks.push_back( link2 );
3644           }
3645         }
3646
3647         // Get nodes of possible quadrangles
3648         const SMDS_MeshNode *n12 [4], *n13 [4];
3649         bool Ok12 = false, Ok13 = false;
3650         const SMDS_MeshNode *linkNode1, *linkNode2;
3651         if(tr2) {
3652           linkNode1 = link12->first;
3653           linkNode2 = link12->second;
3654           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3655             Ok12 = true;
3656         }
3657         if(tr3) {
3658           linkNode1 = link13->first;
3659           linkNode2 = link13->second;
3660           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3661             Ok13 = true;
3662         }
3663
3664         // Choose a pair to fuse
3665         if ( Ok12 && Ok13 ) {
3666           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3667           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3668           double aBadRate12 = getBadRate( &quad12, theCrit );
3669           double aBadRate13 = getBadRate( &quad13, theCrit );
3670           if (  aBadRate13 < aBadRate12 )
3671             Ok12 = false;
3672           else
3673             Ok13 = false;
3674         }
3675
3676         // Make quadrangles
3677         // and remove fused elems and remove links from the maps
3678         mapEl_setLi.erase( tr1 );
3679         if ( Ok12 )
3680         {
3681           mapEl_setLi.erase( tr2 );
3682           mapLi_listEl.erase( *link12 );
3683           if ( tr1->NbNodes() == 3 )
3684           {
3685             const SMDS_MeshElement* newElem = 0;
3686             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3687             myLastCreatedElems.push_back(newElem);
3688             AddToSameGroups( newElem, tr1, aMesh );
3689             int aShapeId = tr1->getshapeId();
3690             if ( aShapeId )
3691               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3692             aMesh->RemoveElement( tr1 );
3693             aMesh->RemoveElement( tr2 );
3694           }
3695           else {
3696             vector< const SMDS_MeshNode* > N1;
3697             vector< const SMDS_MeshNode* > N2;
3698             getNodesFromTwoTria(tr1,tr2,N1,N2);
3699             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3700             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3701             // i.e. first nodes from both arrays form a new diagonal
3702             const SMDS_MeshNode* aNodes[8];
3703             aNodes[0] = N1[0];
3704             aNodes[1] = N1[1];
3705             aNodes[2] = N2[0];
3706             aNodes[3] = N2[1];
3707             aNodes[4] = N1[3];
3708             aNodes[5] = N2[5];
3709             aNodes[6] = N2[3];
3710             aNodes[7] = N1[5];
3711             const SMDS_MeshElement* newElem = 0;
3712             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3713               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3714                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3715             else
3716               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3718             myLastCreatedElems.push_back(newElem);
3719             AddToSameGroups( newElem, tr1, aMesh );
3720             int aShapeId = tr1->getshapeId();
3721             if ( aShapeId )
3722               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3723             aMesh->RemoveElement( tr1 );
3724             aMesh->RemoveElement( tr2 );
3725             // remove middle node (9)
3726             if ( N1[4]->NbInverseElements() == 0 )
3727               aMesh->RemoveNode( N1[4] );
3728             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3729               aMesh->RemoveNode( N1[6] );
3730             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3731               aMesh->RemoveNode( N2[6] );
3732           }
3733         }
3734         else if ( Ok13 )
3735         {
3736           mapEl_setLi.erase( tr3 );
3737           mapLi_listEl.erase( *link13 );
3738           if ( tr1->NbNodes() == 3 ) {
3739             const SMDS_MeshElement* newElem = 0;
3740             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3741             myLastCreatedElems.push_back(newElem);
3742             AddToSameGroups( newElem, tr1, aMesh );
3743             int aShapeId = tr1->getshapeId();
3744             if ( aShapeId )
3745               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3746             aMesh->RemoveElement( tr1 );
3747             aMesh->RemoveElement( tr3 );
3748           }
3749           else {
3750             vector< const SMDS_MeshNode* > N1;
3751             vector< const SMDS_MeshNode* > N2;
3752             getNodesFromTwoTria(tr1,tr3,N1,N2);
3753             // now we receive following N1 and N2 (using numeration as above image)
3754             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3755             // i.e. first nodes from both arrays form a new diagonal
3756             const SMDS_MeshNode* aNodes[8];
3757             aNodes[0] = N1[0];
3758             aNodes[1] = N1[1];
3759             aNodes[2] = N2[0];
3760             aNodes[3] = N2[1];
3761             aNodes[4] = N1[3];
3762             aNodes[5] = N2[5];
3763             aNodes[6] = N2[3];
3764             aNodes[7] = N1[5];
3765             const SMDS_MeshElement* newElem = 0;
3766             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3767               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3768                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3769             else
3770               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3772             myLastCreatedElems.push_back(newElem);
3773             AddToSameGroups( newElem, tr1, aMesh );
3774             int aShapeId = tr1->getshapeId();
3775             if ( aShapeId )
3776               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3777             aMesh->RemoveElement( tr1 );
3778             aMesh->RemoveElement( tr3 );
3779             // remove middle node (9)
3780             if ( N1[4]->NbInverseElements() == 0 )
3781               aMesh->RemoveNode( N1[4] );
3782             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3783               aMesh->RemoveNode( N1[6] );
3784             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3785               aMesh->RemoveNode( N2[6] );
3786           }
3787         }
3788
3789         // Next element to fuse: the rejected one
3790         if ( tr3 )
3791           startElem = Ok12 ? tr3 : tr2;
3792
3793       } // if ( startElem )
3794     } // while ( startElem || !startLinks.empty() )
3795   } // while ( ! mapEl_setLi.empty() )
3796
3797   return true;
3798 }
3799
3800 //================================================================================
3801 /*!
3802  * \brief Return nodes linked to the given one
3803  * \param theNode - the node
3804  * \param linkedNodes - the found nodes
3805  * \param type - the type of elements to check
3806  *
3807  * Medium nodes are ignored
3808  */
3809 //================================================================================
3810
3811 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3812                                        TIDSortedElemSet &   linkedNodes,
3813                                        SMDSAbs_ElementType  type )
3814 {
3815   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3816   while ( elemIt->more() )
3817   {
3818     const SMDS_MeshElement* elem = elemIt->next();
3819     if(elem->GetType() == SMDSAbs_0DElement)
3820       continue;
3821
3822     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3823     if ( elem->GetType() == SMDSAbs_Volume )
3824     {
3825       SMDS_VolumeTool vol( elem );
3826       while ( nodeIt->more() ) {
3827         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3828         if ( theNode != n && vol.IsLinked( theNode, n ))
3829           linkedNodes.insert( n );
3830       }
3831     }
3832     else
3833     {
3834       for ( int i = 0; nodeIt->more(); ++i ) {
3835         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3836         if ( n == theNode ) {
3837           int iBefore = i - 1;
3838           int iAfter  = i + 1;
3839           if ( elem->IsQuadratic() ) {
3840             int nb = elem->NbNodes() / 2;
3841             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3842             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3843           }
3844           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3845           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3846         }
3847       }
3848     }
3849   }
3850 }
3851
3852 //=======================================================================
3853 //function : laplacianSmooth
3854 //purpose  : pulls theNode toward the center of surrounding nodes directly
3855 //           connected to that node along an element edge
3856 //=======================================================================
3857
3858 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3859                      const Handle(Geom_Surface)&          theSurface,
3860                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3861 {
3862   // find surrounding nodes
3863
3864   TIDSortedElemSet nodeSet;
3865   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3866
3867   // compute new coodrs
3868
3869   double coord[] = { 0., 0., 0. };
3870   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3871   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3872     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3873     if ( theSurface.IsNull() ) { // smooth in 3D
3874       coord[0] += node->X();
3875       coord[1] += node->Y();
3876       coord[2] += node->Z();
3877     }
3878     else { // smooth in 2D
3879       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3880       gp_XY* uv = theUVMap[ node ];
3881       coord[0] += uv->X();
3882       coord[1] += uv->Y();
3883     }
3884   }
3885   int nbNodes = nodeSet.size();
3886   if ( !nbNodes )
3887     return;
3888   coord[0] /= nbNodes;
3889   coord[1] /= nbNodes;
3890
3891   if ( !theSurface.IsNull() ) {
3892     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3893     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3894     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3895     coord[0] = p3d.X();
3896     coord[1] = p3d.Y();
3897     coord[2] = p3d.Z();
3898   }
3899   else
3900     coord[2] /= nbNodes;
3901
3902   // move node
3903
3904   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3905 }
3906
3907 //=======================================================================
3908 //function : centroidalSmooth
3909 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3910 //           surrounding elements
3911 //=======================================================================
3912
3913 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3914                       const Handle(Geom_Surface)&          theSurface,
3915                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3916 {
3917   gp_XYZ aNewXYZ(0.,0.,0.);
3918   SMESH::Controls::Area anAreaFunc;
3919   double totalArea = 0.;
3920   int nbElems = 0;
3921
3922   // compute new XYZ
3923
3924   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3925   while ( elemIt->more() )
3926   {
3927     const SMDS_MeshElement* elem = elemIt->next();
3928     nbElems++;
3929
3930     gp_XYZ elemCenter(0.,0.,0.);
3931     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3932     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3933     int nn = elem->NbNodes();
3934     if(elem->IsQuadratic()) nn = nn/2;
3935     int i=0;
3936     //while ( itN->more() ) {
3937     while ( i<nn ) {
3938       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3939       i++;
3940       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3941       aNodePoints.push_back( aP );
3942       if ( !theSurface.IsNull() ) { // smooth in 2D
3943         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3944         gp_XY* uv = theUVMap[ aNode ];
3945         aP.SetCoord( uv->X(), uv->Y(), 0. );
3946       }
3947       elemCenter += aP;
3948     }
3949     double elemArea = anAreaFunc.GetValue( aNodePoints );
3950     totalArea += elemArea;
3951     elemCenter /= nn;
3952     aNewXYZ += elemCenter * elemArea;
3953   }
3954   aNewXYZ /= totalArea;
3955   if ( !theSurface.IsNull() ) {
3956     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3957     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3958   }
3959
3960   // move node
3961
3962   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3963 }
3964
3965 //=======================================================================
3966 //function : getClosestUV
3967 //purpose  : return UV of closest projection
3968 //=======================================================================
3969
3970 static bool getClosestUV (Extrema_GenExtPS& projector,
3971                           const gp_Pnt&     point,
3972                           gp_XY &           result)
3973 {
3974   projector.Perform( point );
3975   if ( projector.IsDone() ) {
3976     double u = 0, v = 0, minVal = DBL_MAX;
3977     for ( int i = projector.NbExt(); i > 0; i-- )
3978       if ( projector.SquareDistance( i ) < minVal ) {
3979         minVal = projector.SquareDistance( i );
3980         projector.Point( i ).Parameter( u, v );
3981       }
3982     result.SetCoord( u, v );
3983     return true;
3984   }
3985   return false;
3986 }
3987
3988 //=======================================================================
3989 //function : Smooth
3990 //purpose  : Smooth theElements during theNbIterations or until a worst
3991 //           element has aspect ratio <= theTgtAspectRatio.
3992 //           Aspect Ratio varies in range [1.0, inf].
3993 //           If theElements is empty, the whole mesh is smoothed.
3994 //           theFixedNodes contains additionally fixed nodes. Nodes built
3995 //           on edges and boundary nodes are always fixed.
3996 //=======================================================================
3997
3998 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3999                                set<const SMDS_MeshNode*> & theFixedNodes,
4000                                const SmoothMethod          theSmoothMethod,
4001                                const int                   theNbIterations,
4002                                double                      theTgtAspectRatio,
4003                                const bool                  the2D)
4004 {
4005   ClearLastCreated();
4006
4007   if ( theTgtAspectRatio < 1.0 )
4008     theTgtAspectRatio = 1.0;
4009
4010   const double disttol = 1.e-16;
4011
4012   SMESH::Controls::AspectRatio aQualityFunc;
4013
4014   SMESHDS_Mesh* aMesh = GetMeshDS();
4015
4016   if ( theElems.empty() ) {
4017     // add all faces to theElems
4018     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4019     while ( fIt->more() ) {
4020       const SMDS_MeshElement* face = fIt->next();
4021       theElems.insert( theElems.end(), face );
4022     }
4023   }
4024   // get all face ids theElems are on
4025   set< int > faceIdSet;
4026   TIDSortedElemSet::iterator itElem;
4027   if ( the2D )
4028     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4029       int fId = FindShape( *itElem );
4030       // check that corresponding submesh exists and a shape is face
4031       if (fId &&
4032           faceIdSet.find( fId ) == faceIdSet.end() &&
4033           aMesh->MeshElements( fId )) {
4034         TopoDS_Shape F = aMesh->IndexToShape( fId );
4035         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4036           faceIdSet.insert( fId );
4037       }
4038     }
4039   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4040
4041   // ===============================================
4042   // smooth elements on each TopoDS_Face separately
4043   // ===============================================
4044
4045   SMESH_MesherHelper helper( *GetMesh() );
4046
4047   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4048   for ( ; fId != faceIdSet.rend(); ++fId )
4049   {
4050     // get face surface and submesh
4051     Handle(Geom_Surface) surface;
4052     SMESHDS_SubMesh* faceSubMesh = 0;
4053     TopoDS_Face face;
4054     double fToler2 = 0;
4055     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4056     bool isUPeriodic = false, isVPeriodic = false;
4057     if ( *fId )
4058     {
4059       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4060       surface = BRep_Tool::Surface( face );
4061       faceSubMesh = aMesh->MeshElements( *fId );
4062       fToler2 = BRep_Tool::Tolerance( face );
4063       fToler2 *= fToler2 * 10.;
4064       isUPeriodic = surface->IsUPeriodic();
4065       // if ( isUPeriodic )
4066       //   surface->UPeriod();
4067       isVPeriodic = surface->IsVPeriodic();
4068       // if ( isVPeriodic )
4069       //   surface->VPeriod();
4070       surface->Bounds( u1, u2, v1, v2 );
4071       helper.SetSubShape( face );
4072     }
4073     // ---------------------------------------------------------
4074     // for elements on a face, find movable and fixed nodes and
4075     // compute UV for them
4076     // ---------------------------------------------------------
4077     bool checkBoundaryNodes = false;
4078     bool isQuadratic = false;
4079     set<const SMDS_MeshNode*> setMovableNodes;
4080     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4081     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4082     list< const SMDS_MeshElement* > elemsOnFace;
4083
4084     Extrema_GenExtPS projector;
4085     GeomAdaptor_Surface surfAdaptor;
4086     if ( !surface.IsNull() ) {
4087       surfAdaptor.Load( surface );
4088       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4089     }
4090     int nbElemOnFace = 0;
4091     itElem = theElems.begin();
4092     // loop on not yet smoothed elements: look for elems on a face
4093     while ( itElem != theElems.end() )
4094     {
4095       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4096         break; // all elements found
4097
4098       const SMDS_MeshElement* elem = *itElem;
4099       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4100            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4101         ++itElem;
4102         continue;
4103       }
4104       elemsOnFace.push_back( elem );
4105       theElems.erase( itElem++ );
4106       nbElemOnFace++;
4107
4108       if ( !isQuadratic )
4109         isQuadratic = elem->IsQuadratic();
4110
4111       // get movable nodes of elem
4112       const SMDS_MeshNode* node;
4113       SMDS_TypeOfPosition posType;
4114       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4115       int nn = 0, nbn =  elem->NbNodes();
4116       if(elem->IsQuadratic())
4117         nbn = nbn/2;
4118       while ( nn++ < nbn ) {
4119         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4120         const SMDS_PositionPtr& pos = node->GetPosition();
4121         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122         if (posType != SMDS_TOP_EDGE &&
4123             posType != SMDS_TOP_VERTEX &&
4124             theFixedNodes.find( node ) == theFixedNodes.end())
4125         {
4126           // check if all faces around the node are on faceSubMesh
4127           // because a node on edge may be bound to face
4128           bool all = true;
4129           if ( faceSubMesh ) {
4130             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4131             while ( eIt->more() && all ) {
4132               const SMDS_MeshElement* e = eIt->next();
4133               all = faceSubMesh->Contains( e );
4134             }
4135           }
4136           if ( all )
4137             setMovableNodes.insert( node );
4138           else
4139             checkBoundaryNodes = true;
4140         }
4141         if ( posType == SMDS_TOP_3DSPACE )
4142           checkBoundaryNodes = true;
4143       }
4144
4145       if ( surface.IsNull() )
4146         continue;
4147
4148       // get nodes to check UV
4149       list< const SMDS_MeshNode* > uvCheckNodes;
4150       const SMDS_MeshNode* nodeInFace = 0;
4151       itN = elem->nodesIterator();
4152       nn = 0; nbn =  elem->NbNodes();
4153       if(elem->IsQuadratic())
4154         nbn = nbn/2;
4155       while ( nn++ < nbn ) {
4156         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4157         if ( node->GetPosition()->GetDim() == 2 )
4158           nodeInFace = node;
4159         if ( uvMap.find( node ) == uvMap.end() )
4160           uvCheckNodes.push_back( node );
4161         // add nodes of elems sharing node
4162         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4163         //         while ( eIt->more() ) {
4164         //           const SMDS_MeshElement* e = eIt->next();
4165         //           if ( e != elem ) {
4166         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4167         //             while ( nIt->more() ) {
4168         //               const SMDS_MeshNode* n =
4169         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4170         //               if ( uvMap.find( n ) == uvMap.end() )
4171         //                 uvCheckNodes.push_back( n );
4172         //             }
4173         //           }
4174         //         }
4175       }
4176       // check UV on face
4177       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4178       for ( ; n != uvCheckNodes.end(); ++n ) {
4179         node = *n;
4180         gp_XY uv( 0, 0 );
4181         const SMDS_PositionPtr& pos = node->GetPosition();
4182         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4183         // get existing UV
4184         if ( pos )
4185         {
4186           bool toCheck = true;
4187           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4188         }
4189         // compute not existing UV
4190         bool project = ( posType == SMDS_TOP_3DSPACE );
4191         // double dist1 = DBL_MAX, dist2 = 0;
4192         // if ( posType != SMDS_TOP_3DSPACE ) {
4193         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4194         //   project = dist1 > fToler2;
4195         // }
4196         if ( project ) { // compute new UV
4197           gp_XY newUV;
4198           gp_Pnt pNode = SMESH_NodeXYZ( node );
4199           if ( !getClosestUV( projector, pNode, newUV )) {
4200             MESSAGE("Node Projection Failed " << node);
4201           }
4202           else {
4203             if ( isUPeriodic )
4204               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4205             if ( isVPeriodic )
4206               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4207             // check new UV
4208             // if ( posType != SMDS_TOP_3DSPACE )
4209             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4210             // if ( dist2 < dist1 )
4211             uv = newUV;
4212           }
4213         }
4214         // store UV in the map
4215         listUV.push_back( uv );
4216         uvMap.insert( make_pair( node, &listUV.back() ));
4217       }
4218     } // loop on not yet smoothed elements
4219
4220     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4221       checkBoundaryNodes = true;
4222
4223     // fix nodes on mesh boundary
4224
4225     if ( checkBoundaryNodes ) {
4226       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4227       map< SMESH_TLink, int >::iterator link_nb;
4228       // put all elements links to linkNbMap
4229       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4230       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4231         const SMDS_MeshElement* elem = (*elemIt);
4232         int nbn =  elem->NbCornerNodes();
4233         // loop on elem links: insert them in linkNbMap
4234         for ( int iN = 0; iN < nbn; ++iN ) {
4235           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4236           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4237           SMESH_TLink link( n1, n2 );
4238           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4239           link_nb->second++;
4240         }
4241       }
4242       // remove nodes that are in links encountered only once from setMovableNodes
4243       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4244         if ( link_nb->second == 1 ) {
4245           setMovableNodes.erase( link_nb->first.node1() );
4246           setMovableNodes.erase( link_nb->first.node2() );
4247         }
4248       }
4249     }
4250
4251     // -----------------------------------------------------
4252     // for nodes on seam edge, compute one more UV ( uvMap2 );
4253     // find movable nodes linked to nodes on seam and which
4254     // are to be smoothed using the second UV ( uvMap2 )
4255     // -----------------------------------------------------
4256
4257     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4258     if ( !surface.IsNull() ) {
4259       TopExp_Explorer eExp( face, TopAbs_EDGE );
4260       for ( ; eExp.More(); eExp.Next() ) {
4261         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4262         if ( !BRep_Tool::IsClosed( edge, face ))
4263           continue;
4264         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4265         if ( !sm ) continue;
4266         // find out which parameter varies for a node on seam
4267         double f,l;
4268         gp_Pnt2d uv1, uv2;
4269         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4270         if ( pcurve.IsNull() ) continue;
4271         uv1 = pcurve->Value( f );
4272         edge.Reverse();
4273         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4274         if ( pcurve.IsNull() ) continue;
4275         uv2 = pcurve->Value( f );
4276         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4277         // assure uv1 < uv2
4278         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4279           std::swap( uv1, uv2 );
4280         // get nodes on seam and its vertices
4281         list< const SMDS_MeshNode* > seamNodes;
4282         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4283         while ( nSeamIt->more() ) {
4284           const SMDS_MeshNode* node = nSeamIt->next();
4285           if ( !isQuadratic || !IsMedium( node ))
4286             seamNodes.push_back( node );
4287         }
4288         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4289         for ( ; vExp.More(); vExp.Next() ) {
4290           sm = aMesh->MeshElements( vExp.Current() );
4291           if ( sm ) {
4292             nSeamIt = sm->GetNodes();
4293             while ( nSeamIt->more() )
4294               seamNodes.push_back( nSeamIt->next() );
4295           }
4296         }
4297         // loop on nodes on seam
4298         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4299         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4300           const SMDS_MeshNode* nSeam = *noSeIt;
4301           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4302           if ( n_uv == uvMap.end() )
4303             continue;
4304           // set the first UV
4305           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4306           // set the second UV
4307           listUV.push_back( *n_uv->second );
4308           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4309           if ( uvMap2.empty() )
4310             uvMap2 = uvMap; // copy the uvMap contents
4311           uvMap2[ nSeam ] = &listUV.back();
4312
4313           // collect movable nodes linked to ones on seam in nodesNearSeam
4314           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4315           while ( eIt->more() ) {
4316             const SMDS_MeshElement* e = eIt->next();
4317             int nbUseMap1 = 0, nbUseMap2 = 0;
4318             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4319             int nn = 0, nbn =  e->NbNodes();
4320             if(e->IsQuadratic()) nbn = nbn/2;
4321             while ( nn++ < nbn )
4322             {
4323               const SMDS_MeshNode* n =
4324                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4325               if (n == nSeam ||
4326                   setMovableNodes.find( n ) == setMovableNodes.end() )
4327                 continue;
4328               // add only nodes being closer to uv2 than to uv1
4329               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4330               //              0.5 * ( n->Y() + nSeam->Y() ),
4331               //              0.5 * ( n->Z() + nSeam->Z() ));
4332               // gp_XY uv;
4333               // getClosestUV( projector, pMid, uv );
4334               double x = uvMap[ n ]->Coord( iPar );
4335               if ( Abs( uv1.Coord( iPar ) - x ) >
4336                    Abs( uv2.Coord( iPar ) - x )) {
4337                 nodesNearSeam.insert( n );
4338                 nbUseMap2++;
4339               }
4340               else
4341                 nbUseMap1++;
4342             }
4343             // for centroidalSmooth all element nodes must
4344             // be on one side of a seam
4345             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4346               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4347               nn = 0;
4348               while ( nn++ < nbn ) {
4349                 const SMDS_MeshNode* n =
4350                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4351                 setMovableNodes.erase( n );
4352               }
4353             }
4354           }
4355         } // loop on nodes on seam
4356       } // loop on edge of a face
4357     } // if ( !face.IsNull() )
4358
4359     if ( setMovableNodes.empty() ) {
4360       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4361       continue; // goto next face
4362     }
4363
4364     // -------------
4365     // SMOOTHING //
4366     // -------------
4367
4368     int it = -1;
4369     double maxRatio = -1., maxDisplacement = -1.;
4370     set<const SMDS_MeshNode*>::iterator nodeToMove;
4371     for ( it = 0; it < theNbIterations; it++ ) {
4372       maxDisplacement = 0.;
4373       nodeToMove = setMovableNodes.begin();
4374       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4375         const SMDS_MeshNode* node = (*nodeToMove);
4376         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4377
4378         // smooth
4379         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4380         if ( theSmoothMethod == LAPLACIAN )
4381           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4382         else
4383           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4384
4385         // node displacement
4386         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4387         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4388         if ( aDispl > maxDisplacement )
4389           maxDisplacement = aDispl;
4390       }
4391       // no node movement => exit
4392       //if ( maxDisplacement < 1.e-16 ) {
4393       if ( maxDisplacement < disttol ) {
4394         MESSAGE("-- no node movement --");
4395         break;
4396       }
4397
4398       // check elements quality
4399       maxRatio  = 0;
4400       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4401       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4402         const SMDS_MeshElement* elem = (*elemIt);
4403         if ( !elem || elem->GetType() != SMDSAbs_Face )
4404           continue;
4405         SMESH::Controls::TSequenceOfXYZ aPoints;
4406         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4407           double aValue = aQualityFunc.GetValue( aPoints );
4408           if ( aValue > maxRatio )
4409             maxRatio = aValue;
4410         }
4411       }
4412       if ( maxRatio <= theTgtAspectRatio ) {
4413         //MESSAGE("-- quality achieved --");
4414         break;
4415       }
4416       if (it+1 == theNbIterations) {
4417         //MESSAGE("-- Iteration limit exceeded --");
4418       }
4419     } // smoothing iterations
4420
4421     // MESSAGE(" Face id: " << *fId <<
4422     //         " Nb iterstions: " << it <<
4423     //         " Displacement: " << maxDisplacement <<
4424     //         " Aspect Ratio " << maxRatio);
4425
4426     // ---------------------------------------
4427     // new nodes positions are computed,
4428     // record movement in DS and set new UV
4429     // ---------------------------------------
4430     nodeToMove = setMovableNodes.begin();
4431     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4432       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4433       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4434       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4435       if ( node_uv != uvMap.end() ) {
4436         gp_XY* uv = node_uv->second;
4437         node->SetPosition
4438           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4439       }
4440     }
4441
4442     // move medium nodes of quadratic elements
4443     if ( isQuadratic )
4444     {
4445       vector<const SMDS_MeshNode*> nodes;
4446       bool checkUV;
4447       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4448       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4449       {
4450         const SMDS_MeshElement* QF = *elemIt;
4451         if ( QF->IsQuadratic() )
4452         {
4453           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4454                         SMDS_MeshElement::iterator() );
4455           nodes.push_back( nodes[0] );
4456           gp_Pnt xyz;
4457           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4458           {
4459             if ( !surface.IsNull() )
4460             {
4461               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4462               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4463               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4464               xyz = surface->Value( uv.X(), uv.Y() );
4465             }
4466             else {
4467               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4468             }
4469             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4470               // we have to move a medium node
4471               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4472           }
4473         }
4474       }
4475     }
4476
4477   } // loop on face ids
4478
4479 }
4480
4481 namespace
4482 {
4483   //=======================================================================
4484   //function : isReverse
4485   //purpose  : Return true if normal of prevNodes is not co-directied with
4486   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4487   //           iNotSame is where prevNodes and nextNodes are different.
4488   //           If result is true then future volume orientation is OK
4489   //=======================================================================
4490
4491   bool isReverse(const SMDS_MeshElement*             face,
4492                  const vector<const SMDS_MeshNode*>& prevNodes,
4493                  const vector<const SMDS_MeshNode*>& nextNodes,
4494                  const int                           iNotSame)
4495   {
4496
4497     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4498     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4499     gp_XYZ extrDir( pN - pP ), faceNorm;
4500     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4501
4502     return faceNorm * extrDir < 0.0;
4503   }
4504
4505   //================================================================================
4506   /*!
4507    * \brief Assure that theElemSets[0] holds elements, not nodes
4508    */
4509   //================================================================================
4510
4511   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4512   {
4513     if ( !theElemSets[0].empty() &&
4514          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4515     {
4516       std::swap( theElemSets[0], theElemSets[1] );
4517     }
4518     else if ( !theElemSets[1].empty() &&
4519               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4520     {
4521       std::swap( theElemSets[0], theElemSets[1] );
4522     }
4523   }
4524 }
4525
4526 //=======================================================================
4527 /*!
4528  * \brief Create elements by sweeping an element
4529  * \param elem - element to sweep
4530  * \param newNodesItVec - nodes generated from each node of the element
4531  * \param newElems - generated elements
4532  * \param nbSteps - number of sweeping steps
4533  * \param srcElements - to append elem for each generated element
4534  */
4535 //=======================================================================
4536
4537 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4538                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4539                                     list<const SMDS_MeshElement*>&        newElems,
4540                                     const size_t                          nbSteps,
4541                                     SMESH_SequenceOfElemPtr&              srcElements)
4542 {
4543   SMESHDS_Mesh* aMesh = GetMeshDS();
4544
4545   const int           nbNodes = elem->NbNodes();
4546   const int         nbCorners = elem->NbCornerNodes();
4547   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4548                                                           polyhedron creation !!! */
4549   // Loop on elem nodes:
4550   // find new nodes and detect same nodes indices
4551   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4552   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4553   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4554   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4555
4556   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4557   vector<int> sames(nbNodes);
4558   vector<bool> isSingleNode(nbNodes);
4559
4560   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4561     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4562     const SMDS_MeshNode*                         node = nnIt->first;
4563     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4564     if ( listNewNodes.empty() )
4565       return;
4566
4567     itNN   [ iNode ] = listNewNodes.begin();
4568     prevNod[ iNode ] = node;
4569     nextNod[ iNode ] = listNewNodes.front();
4570
4571     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4572                                                              corner node of linear */
4573     if ( prevNod[ iNode ] != nextNod [ iNode ])
4574       nbDouble += !isSingleNode[iNode];
4575
4576     if( iNode < nbCorners ) { // check corners only
4577       if ( prevNod[ iNode ] == nextNod [ iNode ])
4578         sames[nbSame++] = iNode;
4579       else
4580         iNotSameNode = iNode;
4581     }
4582   }
4583
4584   if ( nbSame == nbNodes || nbSame > 2) {
4585     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4586     return;
4587   }
4588
4589   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4590   {
4591     // fix nodes order to have bottom normal external
4592     if ( baseType == SMDSEntity_Polygon )
4593     {
4594       std::reverse( itNN.begin(), itNN.end() );
4595       std::reverse( prevNod.begin(), prevNod.end() );
4596       std::reverse( midlNod.begin(), midlNod.end() );
4597       std::reverse( nextNod.begin(), nextNod.end() );
4598       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4599     }
4600     else
4601     {
4602       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4603       SMDS_MeshCell::applyInterlace( ind, itNN );
4604       SMDS_MeshCell::applyInterlace( ind, prevNod );
4605       SMDS_MeshCell::applyInterlace( ind, nextNod );
4606       SMDS_MeshCell::applyInterlace( ind, midlNod );
4607       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4608       if ( nbSame > 0 )
4609       {
4610         sames[nbSame] = iNotSameNode;
4611         for ( int j = 0; j <= nbSame; ++j )
4612           for ( size_t i = 0; i < ind.size(); ++i )
4613             if ( ind[i] == sames[j] )
4614             {
4615               sames[j] = i;
4616               break;
4617             }
4618         iNotSameNode = sames[nbSame];
4619       }
4620     }
4621   }
4622   else if ( elem->GetType() == SMDSAbs_Edge )
4623   {
4624     // orient a new face same as adjacent one
4625     int i1, i2;
4626     const SMDS_MeshElement* e;
4627     TIDSortedElemSet dummy;
4628     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4629         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4630         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4631     {
4632       // there is an adjacent face, check order of nodes in it
4633       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4634       if ( sameOrder )
4635       {
4636         std::swap( itNN[0],    itNN[1] );
4637         std::swap( prevNod[0], prevNod[1] );
4638         std::swap( nextNod[0], nextNod[1] );
4639         std::swap( isSingleNode[0], isSingleNode[1] );
4640         if ( nbSame > 0 )
4641           sames[0] = 1 - sames[0];
4642         iNotSameNode = 1 - iNotSameNode;
4643       }
4644     }
4645   }
4646
4647   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4648   if ( nbSame > 0 ) {
4649     iSameNode    = sames[ nbSame-1 ];
4650     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4651     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4652     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4653   }
4654
4655   if ( baseType == SMDSEntity_Polygon )
4656   {
4657     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4658     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4659   }
4660   else if ( baseType == SMDSEntity_Quad_Polygon )
4661   {
4662     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4663     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4664   }
4665
4666   // make new elements
4667   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4668   {
4669     // get next nodes
4670     for ( iNode = 0; iNode < nbNodes; iNode++ )
4671     {
4672       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4673       nextNod[ iNode ] = *itNN[ iNode ]++;
4674     }
4675
4676     SMDS_MeshElement* aNewElem = 0;
4677     /*if(!elem->IsPoly())*/ {
4678       switch ( baseType ) {
4679       case SMDSEntity_0D:
4680       case SMDSEntity_Node: { // sweep NODE
4681         if ( nbSame == 0 ) {
4682           if ( isSingleNode[0] )
4683             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4684           else
4685             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4686         }
4687         else
4688           return;
4689         break;
4690       }
4691       case SMDSEntity_Edge: { // sweep EDGE
4692         if ( nbDouble == 0 )
4693         {
4694           if ( nbSame == 0 ) // ---> quadrangle
4695             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4696                                       nextNod[ 1 ], nextNod[ 0 ] );
4697           else               // ---> triangle
4698             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4699                                       nextNod[ iNotSameNode ] );
4700         }
4701         else                 // ---> polygon
4702         {
4703           vector<const SMDS_MeshNode*> poly_nodes;
4704           poly_nodes.push_back( prevNod[0] );
4705           poly_nodes.push_back( prevNod[1] );
4706           if ( prevNod[1] != nextNod[1] )
4707           {
4708             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4709             poly_nodes.push_back( nextNod[1] );
4710           }
4711           if ( prevNod[0] != nextNod[0] )
4712           {
4713             poly_nodes.push_back( nextNod[0] );
4714             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4715           }
4716           switch ( poly_nodes.size() ) {
4717           case 3:
4718             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4719             break;
4720           case 4:
4721             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4722                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4723             break;
4724           default:
4725             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4726           }
4727         }
4728         break;
4729       }
4730       case SMDSEntity_Triangle: // TRIANGLE --->
4731       {
4732         if ( nbDouble > 0 ) break;
4733         if ( nbSame == 0 )       // ---> pentahedron
4734           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4735                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4736
4737         else if ( nbSame == 1 )  // ---> pyramid
4738           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4739                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4740                                        nextNod[ iSameNode ]);
4741
4742         else // 2 same nodes:       ---> tetrahedron
4743           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4744                                        nextNod[ iNotSameNode ]);
4745         break;
4746       }
4747       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4748       {
4749         if ( nbSame == 2 )
4750           return;
4751         if ( nbDouble+nbSame == 2 )
4752         {
4753           if(nbSame==0) {      // ---> quadratic quadrangle
4754             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4755                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4756           }
4757           else { //(nbSame==1) // ---> quadratic triangle
4758             if(sames[0]==2) {
4759               return; // medium node on axis
4760             }
4761             else if(sames[0]==0)
4762               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4763                                         prevNod[2], midlNod[1], nextNod[2] );
4764             else // sames[0]==1
4765               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4766                                         prevNod[2], nextNod[2], midlNod[0]);
4767           }
4768         }
4769         else if ( nbDouble == 3 )
4770         {
4771           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4772             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4773                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4774           }
4775         }
4776         else
4777           return;
4778         break;
4779       }
4780       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4781         if ( nbDouble > 0 ) break;
4782
4783         if ( nbSame == 0 )       // ---> hexahedron
4784           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4785                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4786
4787         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4788           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4789                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4790                                        nextNod[ iSameNode ]);
4791           newElems.push_back( aNewElem );
4792           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4793                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4794                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4795         }
4796         else if ( nbSame == 2 ) { // ---> pentahedron
4797           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4798             // iBeforeSame is same too
4799             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4800                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4801                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4802           else
4803             // iAfterSame is same too
4804             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4805                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4806                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4807         }
4808         break;
4809       }
4810       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4811       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4812         if ( nbDouble+nbSame != 3 ) break;
4813         if(nbSame==0) {
4814           // --->  pentahedron with 15 nodes
4815           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4816                                        nextNod[0], nextNod[1], nextNod[2],
4817                                        prevNod[3], prevNod[4], prevNod[5],
4818                                        nextNod[3], nextNod[4], nextNod[5],
4819                                        midlNod[0], midlNod[1], midlNod[2]);
4820         }
4821         else if(nbSame==1) {
4822           // --->  2d order pyramid of 13 nodes
4823           int apex = iSameNode;
4824           int i0 = ( apex + 1 ) % nbCorners;
4825           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4826           int i0a = apex + 3;
4827           int i1a = i1 + 3;
4828           int i01 = i0 + 3;
4829           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4830                                       nextNod[i0], nextNod[i1], prevNod[apex],
4831                                       prevNod[i01], midlNod[i0],
4832                                       nextNod[i01], midlNod[i1],
4833                                       prevNod[i1a], prevNod[i0a],
4834                                       nextNod[i0a], nextNod[i1a]);
4835         }
4836         else if(nbSame==2) {
4837           // --->  2d order tetrahedron of 10 nodes
4838           int n1 = iNotSameNode;
4839           int n2 = ( n1 + 1             ) % nbCorners;
4840           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4841           int n12 = n1 + 3;
4842           int n23 = n2 + 3;
4843           int n31 = n3 + 3;
4844           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4845                                        prevNod[n12], prevNod[n23], prevNod[n31],
4846                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4847         }
4848         break;
4849       }
4850       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4851         if( nbSame == 0 ) {
4852           if ( nbDouble != 4 ) break;
4853           // --->  hexahedron with 20 nodes
4854           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4855                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4856                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4857                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4858                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4859         }
4860         else if(nbSame==1) {
4861           // ---> pyramid + pentahedron - can not be created since it is needed
4862           // additional middle node at the center of face
4863           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4864           return;
4865         }
4866         else if( nbSame == 2 ) {
4867           if ( nbDouble != 2 ) break;
4868           // --->  2d order Pentahedron with 15 nodes
4869           int n1,n2,n4,n5;
4870           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4871             // iBeforeSame is same too
4872             n1 = iBeforeSame;
4873             n2 = iOpposSame;
4874             n4 = iSameNode;
4875             n5 = iAfterSame;
4876           }
4877           else {
4878             // iAfterSame is same too
4879             n1 = iSameNode;
4880             n2 = iBeforeSame;
4881             n4 = iAfterSame;
4882             n5 = iOpposSame;
4883           }
4884           int n12 = n2 + 4;
4885           int n45 = n4 + 4;
4886           int n14 = n1 + 4;
4887           int n25 = n5 + 4;
4888           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4889                                        prevNod[n4], prevNod[n5], nextNod[n5],
4890                                        prevNod[n12], midlNod[n2], nextNod[n12],
4891                                        prevNod[n45], midlNod[n5], nextNod[n45],
4892                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4893         }
4894         break;
4895       }
4896       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4897
4898         if( nbSame == 0 && nbDouble == 9 ) {
4899           // --->  tri-quadratic hexahedron with 27 nodes
4900           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4901                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4902                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4903                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4904                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4905                                        prevNod[8], // bottom center
4906                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4907                                        nextNod[8], // top center
4908                                        midlNod[8]);// elem center
4909         }
4910         else
4911         {
4912           return;
4913         }
4914         break;
4915       }
4916       case SMDSEntity_Polygon: { // sweep POLYGON
4917
4918         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4919           // --->  hexagonal prism
4920           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4921                                        prevNod[3], prevNod[4], prevNod[5],
4922                                        nextNod[0], nextNod[1], nextNod[2],
4923                                        nextNod[3], nextNod[4], nextNod[5]);
4924         }
4925         break;
4926       }
4927       case SMDSEntity_Ball:
4928         return;
4929
4930       default:
4931         break;
4932       } // switch ( baseType )
4933     } // scope
4934
4935     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4936     {
4937       if ( baseType != SMDSEntity_Polygon )
4938       {
4939         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4940         SMDS_MeshCell::applyInterlace( ind, prevNod );
4941         SMDS_MeshCell::applyInterlace( ind, nextNod );
4942         SMDS_MeshCell::applyInterlace( ind, midlNod );
4943         SMDS_MeshCell::applyInterlace( ind, itNN );
4944         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4945         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4946       }
4947       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4948       vector<int> quantities (nbNodes + 2);
4949       polyedre_nodes.clear();
4950       quantities.clear();
4951
4952       // bottom of prism
4953       for (int inode = 0; inode < nbNodes; inode++)
4954         polyedre_nodes.push_back( prevNod[inode] );
4955       quantities.push_back( nbNodes );
4956
4957       // top of prism
4958       polyedre_nodes.push_back( nextNod[0] );
4959       for (int inode = nbNodes; inode-1; --inode )
4960         polyedre_nodes.push_back( nextNod[inode-1] );
4961       quantities.push_back( nbNodes );
4962
4963       // side faces
4964       // 3--6--2
4965       // |     |
4966       // 7     5
4967       // |     |
4968       // 0--4--1
4969       const int iQuad = elem->IsQuadratic();
4970       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4971       {
4972         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4973         int inextface = (iface+1+iQuad) % nbNodes;
4974         int imid      = (iface+1) % nbNodes;
4975         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4976         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4977         polyedre_nodes.push_back( prevNod[iface] );             // 1
4978         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4979         {
4980           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4981           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4982         }
4983         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4984         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4985         {
4986           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4987           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4988         }
4989         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4990         if ( nbFaceNodes > 2 )
4991           quantities.push_back( nbFaceNodes );
4992         else // degenerated face
4993           polyedre_nodes.resize( prevNbNodes );
4994       }
4995       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4996
4997     } // try to create a polyherdal prism
4998
4999     if ( aNewElem ) {
5000       newElems.push_back( aNewElem );
5001       myLastCreatedElems.push_back(aNewElem);
5002       srcElements.push_back( elem );
5003     }
5004
5005     // set new prev nodes
5006     for ( iNode = 0; iNode < nbNodes; iNode++ )
5007       prevNod[ iNode ] = nextNod[ iNode ];
5008
5009   } // loop on steps
5010 }
5011
5012 //=======================================================================
5013 /*!
5014  * \brief Create 1D and 2D elements around swept elements
5015  * \param mapNewNodes - source nodes and ones generated from them
5016  * \param newElemsMap - source elements and ones generated from them
5017  * \param elemNewNodesMap - nodes generated from each node of each element
5018  * \param elemSet - all swept elements
5019  * \param nbSteps - number of sweeping steps
5020  * \param srcElements - to append elem for each generated element
5021  */
5022 //=======================================================================
5023
5024 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
5025                                   TTElemOfElemListMap &    newElemsMap,
5026                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
5027                                   TIDSortedElemSet&        elemSet,
5028                                   const int                nbSteps,
5029                                   SMESH_SequenceOfElemPtr& srcElements)
5030 {
5031   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5032   SMESHDS_Mesh* aMesh = GetMeshDS();
5033
5034   // Find nodes belonging to only one initial element - sweep them into edges.
5035
5036   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5037   for ( ; nList != mapNewNodes.end(); nList++ )
5038   {
5039     const SMDS_MeshNode* node =
5040       static_cast<const SMDS_MeshNode*>( nList->first );
5041     if ( newElemsMap.count( node ))
5042       continue; // node was extruded into edge
5043     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5044     int nbInitElems = 0;
5045     const SMDS_MeshElement* el = 0;
5046     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5047     while ( eIt->more() && nbInitElems < 2 ) {
5048       const SMDS_MeshElement* e = eIt->next();
5049       SMDSAbs_ElementType  type = e->GetType();
5050       if ( type == SMDSAbs_Volume ||
5051            type < highType ||
5052            !elemSet.count(e))
5053         continue;
5054       if ( type > highType ) {
5055         nbInitElems = 0;
5056         highType    = type;
5057       }
5058       el = e;
5059       ++nbInitElems;
5060     }
5061     if ( nbInitElems == 1 ) {
5062       bool NotCreateEdge = el && el->IsMediumNode(node);
5063       if(!NotCreateEdge) {
5064         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5065         list<const SMDS_MeshElement*> newEdges;
5066         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5067       }
5068     }
5069   }
5070
5071   // Make a ceiling for each element ie an equal element of last new nodes.
5072   // Find free links of faces - make edges and sweep them into faces.
5073
5074   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5075
5076   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5077   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5078   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5079   {
5080     const SMDS_MeshElement* elem = itElem->first;
5081     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5082
5083     if(itElem->second.size()==0) continue;
5084
5085     const bool isQuadratic = elem->IsQuadratic();
5086
5087     if ( elem->GetType() == SMDSAbs_Edge ) {
5088       // create a ceiling edge
5089       if ( !isQuadratic ) {
5090         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5091                                vecNewNodes[ 1 ]->second.back())) {
5092           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5093                                                       vecNewNodes[ 1 ]->second.back()));
5094           srcElements.push_back( elem );
5095         }
5096       }
5097       else {
5098         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5099                                vecNewNodes[ 1 ]->second.back(),
5100                                vecNewNodes[ 2 ]->second.back())) {
5101           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5102                                                       vecNewNodes[ 1 ]->second.back(),
5103                                                       vecNewNodes[ 2 ]->second.back()));
5104           srcElements.push_back( elem );
5105         }
5106       }
5107     }
5108     if ( elem->GetType() != SMDSAbs_Face )
5109       continue;
5110
5111     bool hasFreeLinks = false;
5112
5113     TIDSortedElemSet avoidSet;
5114     avoidSet.insert( elem );
5115
5116     set<const SMDS_MeshNode*> aFaceLastNodes;
5117     int iNode, nbNodes = vecNewNodes.size();
5118     if ( !isQuadratic ) {
5119       // loop on the face nodes
5120       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5122         // look for free links of the face
5123         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5124         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5125         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5126         // check if a link n1-n2 is free
5127         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5128           hasFreeLinks = true;
5129           // make a new edge and a ceiling for a new edge
5130           const SMDS_MeshElement* edge;
5131           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5132             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5133             srcElements.push_back( myLastCreatedElems.back() );
5134           }
5135           n1 = vecNewNodes[ iNode ]->second.back();
5136           n2 = vecNewNodes[ iNext ]->second.back();
5137           if ( !aMesh->FindEdge( n1, n2 )) {
5138             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5139             srcElements.push_back( edge );
5140           }
5141         }
5142       }
5143     }
5144     else { // elem is quadratic face
5145       int nbn = nbNodes/2;
5146       for ( iNode = 0; iNode < nbn; iNode++ ) {
5147         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5148         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5149         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5150         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5151         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5152         // check if a link is free
5153         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5154              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5155              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5156           hasFreeLinks = true;
5157           // make an edge and a ceiling for a new edge
5158           // find medium node
5159           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5160             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5161             srcElements.push_back( elem );
5162           }
5163           n1 = vecNewNodes[ iNode ]->second.back();
5164           n2 = vecNewNodes[ iNext ]->second.back();
5165           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5166           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5167             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5168             srcElements.push_back( elem );
5169           }
5170         }
5171       }
5172       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5173         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5174       }
5175     }
5176
5177     // sweep free links into faces
5178
5179     if ( hasFreeLinks ) {
5180       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5181       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5182
5183       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5184       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5185       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5186         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5187         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5188       }
5189       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5190         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5191         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5192       }
5193       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5194         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5195         std::advance( v, volNb );
5196         // find indices of free faces of a volume and their source edges
5197         list< int > freeInd;
5198         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5199         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5200         int iF, nbF = vTool.NbFaces();
5201         for ( iF = 0; iF < nbF; iF ++ ) {
5202           if ( vTool.IsFreeFace( iF ) &&
5203                vTool.GetFaceNodes( iF, faceNodeSet ) &&
5204                initNodeSet != faceNodeSet) // except an initial face
5205           {
5206             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5207               continue;
5208             if ( faceNodeSet == initNodeSetNoCenter )
5209               continue;
5210             freeInd.push_back( iF );
5211             // find source edge of a free face iF
5212             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5213             vector<const SMDS_MeshNode*>::iterator lastCommom;
5214             commonNodes.resize( nbNodes, 0 );
5215             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5216                                                 initNodeSet.begin(), initNodeSet.end(),
5217                                                 commonNodes.begin());
5218             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5219               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5220             else
5221               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5222 #ifdef _DEBUG_
5223             if ( !srcEdges.back() )
5224             {
5225               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5226                    << iF << " of volume #" << vTool.ID() << endl;
5227             }
5228 #endif
5229           }
5230         }
5231         if ( freeInd.empty() )
5232           continue;
5233
5234         // create wall faces for all steps;
5235         // if such a face has been already created by sweep of edge,
5236         // assure that its orientation is OK
5237         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5238         {
5239           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5240           vTool.SetExternalNormal();
5241           const int nextShift = vTool.IsForward() ? +1 : -1;
5242           list< int >::iterator ind = freeInd.begin();
5243           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5244           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5245           {
5246             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5247             int nbn = vTool.NbFaceNodes( *ind );
5248             const SMDS_MeshElement * f = 0;
5249             if ( nbn == 3 )              ///// triangle
5250             {
5251               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5252               if ( !f ||
5253                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5254               {
5255                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5256                                                      nodes[ 1 ],
5257                                                      nodes[ 1 + nextShift ] };
5258                 if ( f )
5259                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5260                 else
5261                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5262                                                                newOrder[ 2 ] ));
5263               }
5264             }
5265             else if ( nbn == 4 )       ///// quadrangle
5266             {
5267               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5268               if ( !f ||
5269                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5270               {
5271                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5272                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5273                 if ( f )
5274                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5275                 else
5276                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5277                                                                newOrder[ 2 ], newOrder[ 3 ]));
5278               }
5279             }
5280             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5281             {
5282               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5283               if ( !f ||
5284                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5285               {
5286                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5287                                                      nodes[2],
5288                                                      nodes[2 + 2*nextShift],
5289                                                      nodes[3 - 2*nextShift],
5290                                                      nodes[3],
5291                                                      nodes[3 + 2*nextShift]};
5292                 if ( f )
5293                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5294                 else
5295                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5296                                                                newOrder[ 1 ],
5297                                                                newOrder[ 2 ],
5298                                                                newOrder[ 3 ],
5299                                                                newOrder[ 4 ],
5300                                                                newOrder[ 5 ] ));
5301               }
5302             }
5303             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5304             {
5305               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5306                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5307               if ( !f ||
5308                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5309               {
5310                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5311                                                      nodes[4 - 2*nextShift],
5312                                                      nodes[4],
5313                                                      nodes[4 + 2*nextShift],
5314                                                      nodes[1],
5315                                                      nodes[5 - 2*nextShift],
5316                                                      nodes[5],
5317                                                      nodes[5 + 2*nextShift] };
5318                 if ( f )
5319                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5320                 else
5321                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5322                                                               newOrder[ 2 ], newOrder[ 3 ],
5323                                                               newOrder[ 4 ], newOrder[ 5 ],
5324                                                               newOrder[ 6 ], newOrder[ 7 ]));
5325               }
5326             }
5327             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5328             {
5329               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5330                                       SMDSAbs_Face, /*noMedium=*/false);
5331               if ( !f ||
5332                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5333               {
5334                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5335                                                      nodes[4 - 2*nextShift],
5336                                                      nodes[4],
5337                                                      nodes[4 + 2*nextShift],
5338                                                      nodes[1],
5339                                                      nodes[5 - 2*nextShift],
5340                                                      nodes[5],
5341                                                      nodes[5 + 2*nextShift],
5342                                                      nodes[8] };
5343                 if ( f )
5344                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5345                 else
5346                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5347                                                               newOrder[ 2 ], newOrder[ 3 ],
5348                                                               newOrder[ 4 ], newOrder[ 5 ],
5349                                                               newOrder[ 6 ], newOrder[ 7 ],
5350                                                               newOrder[ 8 ]));
5351               }
5352             }
5353             else  //////// polygon
5354             {
5355               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5356               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5357               if ( !f ||
5358                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5359               {
5360                 if ( !vTool.IsForward() )
5361                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5362                 if ( f )
5363                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5364                 else
5365                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5366               }
5367             }
5368
5369             while ( srcElements.size() < myLastCreatedElems.size() )
5370               srcElements.push_back( *srcEdge );
5371
5372           }  // loop on free faces
5373
5374           // go to the next volume
5375           iVol = 0;
5376           while ( iVol++ < nbVolumesByStep ) v++;
5377
5378         } // loop on steps
5379       } // loop on volumes of one step
5380     } // sweep free links into faces
5381
5382     // Make a ceiling face with a normal external to a volume
5383
5384     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5385     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5386     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5387
5388     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5389       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5390       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5391     }
5392     if ( iF >= 0 )
5393     {
5394       lastVol.SetExternalNormal();
5395       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5396       const               int nbn = lastVol.NbFaceNodes( iF );
5397       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5398       if ( !hasFreeLinks ||
5399            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5400       {
5401         const vector<int>& interlace =
5402           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5403         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5404
5405         AddElement( nodeVec, anyFace.Init( elem ));
5406
5407         while ( srcElements.size() < myLastCreatedElems.size() )
5408           srcElements.push_back( elem );
5409       }
5410     }
5411   } // loop on swept elements
5412 }
5413
5414 //=======================================================================
5415 //function : RotationSweep
5416 //purpose  :
5417 //=======================================================================
5418
5419 SMESH_MeshEditor::PGroupIDs
5420 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5421                                 const gp_Ax1&      theAxis,
5422                                 const double       theAngle,
5423                                 const int          theNbSteps,
5424                                 const double       theTol,
5425                                 const bool         theMakeGroups,
5426                                 const bool         theMakeWalls)
5427 {
5428   ClearLastCreated();
5429
5430   setElemsFirst( theElemSets );
5431   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5432   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5433
5434   // source elements for each generated one
5435   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5436   srcElems.reserve( theElemSets[0].size() );
5437   srcNodes.reserve( theElemSets[1].size() );
5438
5439   gp_Trsf aTrsf;
5440   aTrsf.SetRotation( theAxis, theAngle );
5441   gp_Trsf aTrsf2;
5442   aTrsf2.SetRotation( theAxis, theAngle/2. );
5443
5444   gp_Lin aLine( theAxis );
5445   double aSqTol = theTol * theTol;
5446
5447   SMESHDS_Mesh* aMesh = GetMeshDS();
5448
5449   TNodeOfNodeListMap mapNewNodes;
5450   TElemOfVecOfNnlmiMap mapElemNewNodes;
5451   TTElemOfElemListMap newElemsMap;
5452
5453   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5454                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5455                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5456   // loop on theElemSets
5457   TIDSortedElemSet::iterator itElem;
5458   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5459   {
5460     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5461     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5462       const SMDS_MeshElement* elem = *itElem;
5463       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5464         continue;
5465       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5466       newNodesItVec.reserve( elem->NbNodes() );
5467
5468       // loop on elem nodes
5469       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5470       while ( itN->more() )
5471       {
5472         const SMDS_MeshNode* node = cast2Node( itN->next() );
5473
5474         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5475         double coord[3];
5476         aXYZ.Coord( coord[0], coord[1], coord[2] );
5477         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5478
5479         // check if a node has been already sweeped
5480         TNodeOfNodeListMapItr nIt =
5481           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5482         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5483         if ( listNewNodes.empty() )
5484         {
5485           // check if we are to create medium nodes between corner ones
5486           bool needMediumNodes = false;
5487           if ( isQuadraticMesh )
5488           {
5489             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5490             while (it->more() && !needMediumNodes )
5491             {
5492               const SMDS_MeshElement* invElem = it->next();
5493               if ( invElem != elem && !theElems.count( invElem )) continue;
5494               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5495               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5496                 needMediumNodes = true;
5497             }
5498           }
5499
5500           // make new nodes
5501           const SMDS_MeshNode * newNode = node;
5502           for ( int i = 0; i < theNbSteps; i++ ) {
5503             if ( !isOnAxis ) {
5504               if ( needMediumNodes )  // create a medium node
5505               {
5506                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5507                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5508                 myLastCreatedNodes.push_back(newNode);
5509                 srcNodes.push_back( node );
5510                 listNewNodes.push_back( newNode );
5511                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5512               }
5513               else {
5514                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5515               }
5516               // create a corner node
5517               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5518               myLastCreatedNodes.push_back(newNode);
5519               srcNodes.push_back( node );
5520               listNewNodes.push_back( newNode );
5521             }
5522             else {
5523               listNewNodes.push_back( newNode );
5524               // if ( needMediumNodes )
5525               //   listNewNodes.push_back( newNode );
5526             }
5527           }
5528         }
5529         newNodesItVec.push_back( nIt );
5530       }
5531       // make new elements
5532       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5533     }
5534   }
5535
5536   if ( theMakeWalls )
5537     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5538
5539   PGroupIDs newGroupIDs;
5540   if ( theMakeGroups )
5541     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5542
5543   return newGroupIDs;
5544 }
5545
5546 //=======================================================================
5547 //function : ExtrusParam
5548 //purpose  : standard construction
5549 //=======================================================================
5550
5551 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5552                                             const int                theNbSteps,
5553                                             const std::list<double>& theScales,
5554                                             const std::list<double>& theAngles,
5555                                             const gp_XYZ*            theBasePoint,
5556                                             const int                theFlags,
5557                                             const double             theTolerance):
5558   myDir( theStep ),
5559   myBaseP( Precision::Infinite(), 0, 0 ),
5560   myFlags( theFlags ),
5561   myTolerance( theTolerance ),
5562   myElemsToUse( NULL )
5563 {
5564   mySteps = new TColStd_HSequenceOfReal;
5565   const double stepSize = theStep.Magnitude();
5566   for (int i=1; i<=theNbSteps; i++ )
5567     mySteps->Append( stepSize );
5568
5569   if ( !theScales.empty() )
5570   {
5571     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5572       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5573
5574     // add medium scales
5575     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5576     myScales.reserve( theNbSteps * 2 );
5577     myScales.push_back( 0.5 * ( *s1 + 1. ));
5578     myScales.push_back( *s1 );
5579     for ( ; s2 != theScales.end(); s1 = s2++ )
5580     {
5581       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5582       myScales.push_back( *s2 );
5583     }
5584   }
5585
5586   if ( !theAngles.empty() )
5587   {
5588     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5589     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5590       linearAngleVariation( theNbSteps, angles );
5591
5592     // accumulate angles
5593     double angle = 0;
5594     int nbAngles = 0;
5595     std::list<double>::iterator a1 = angles.begin(), a2;
5596     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5597     {
5598       angle += *a1;
5599       *a1 = angle;
5600     }
5601     while ( nbAngles++ < theNbSteps )
5602       angles.push_back( angles.back() );
5603
5604     // add medium angles
5605     a2 = angles.begin(), a1 = a2++;
5606     myAngles.push_back( 0.5 * *a1 );
5607     myAngles.push_back( *a1 );
5608     for ( ; a2 != angles.end(); a1 = a2++ )
5609     {
5610       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5611       myAngles.push_back( *a2 );
5612     }
5613   }
5614
5615   if ( theBasePoint )
5616   {
5617     myBaseP = *theBasePoint;
5618   }
5619
5620   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5621       ( theTolerance > 0 ))
5622   {
5623     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5624   }
5625   else
5626   {
5627     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5628   }
5629 }
5630
5631 //=======================================================================
5632 //function : ExtrusParam
5633 //purpose  : steps are given explicitly
5634 //=======================================================================
5635
5636 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5637                                             Handle(TColStd_HSequenceOfReal) theSteps,
5638                                             const int                       theFlags,
5639                                             const double                    theTolerance):
5640   myDir( theDir ),
5641   mySteps( theSteps ),
5642   myFlags( theFlags ),
5643   myTolerance( theTolerance ),
5644   myElemsToUse( NULL )
5645 {
5646   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5647       ( theTolerance > 0 ))
5648   {
5649     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5650   }
5651   else
5652   {
5653     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5654   }
5655 }
5656
5657 //=======================================================================
5658 //function : ExtrusParam
5659 //purpose  : for extrusion by normal
5660 //=======================================================================
5661
5662 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5663                                             const int    theNbSteps,
5664                                             const int    theFlags,
5665                                             const int    theDim ):
5666   myDir( 1,0,0 ),
5667   mySteps( new TColStd_HSequenceOfReal ),
5668   myFlags( theFlags ),
5669   myTolerance( 0 ),
5670   myElemsToUse( NULL )
5671 {
5672   for (int i = 0; i < theNbSteps; i++ )
5673     mySteps->Append( theStepSize );
5674
5675   if ( theDim == 1 )
5676   {
5677     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5678   }
5679   else
5680   {
5681     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5682   }
5683 }
5684
5685 //=======================================================================
5686 //function : ExtrusParam
5687 //purpose  : for extrusion along path
5688 //=======================================================================
5689
5690 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5691                                             const gp_Pnt*                   theBasePoint,
5692                                             const std::list<double>&        theScales,
5693                                             const bool                      theMakeGroups )
5694   : myBaseP( Precision::Infinite(), 0, 0 ),
5695     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5696     myPathPoints( thePoints )
5697 {
5698   if ( theBasePoint )
5699   {
5700     myBaseP = theBasePoint->XYZ();
5701   }
5702
5703   if ( !theScales.empty() )
5704   {
5705     // add medium scales
5706     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5707     myScales.reserve( thePoints.size() * 2 );
5708     myScales.push_back( 0.5 * ( 1. + *s1 ));
5709     myScales.push_back( *s1 );
5710     for ( ; s2 != theScales.end(); s1 = s2++ )
5711     {
5712       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5713       myScales.push_back( *s2 );
5714     }
5715   }
5716
5717   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5718 }
5719
5720 //=======================================================================
5721 //function : ExtrusParam::SetElementsToUse
5722 //purpose  : stores elements to use for extrusion by normal, depending on
5723 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5724 //           define myBaseP for scaling
5725 //=======================================================================
5726
5727 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5728                                                       const TIDSortedElemSet& nodes )
5729 {
5730   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5731
5732   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5733   {
5734     myBaseP.SetCoord( 0.,0.,0. );
5735     TIDSortedElemSet newNodes;
5736
5737     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5738     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5739     {
5740       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5741       TIDSortedElemSet::const_iterator itElem = elements.begin();
5742       for ( ; itElem != elements.end(); itElem++ )
5743       {
5744         const SMDS_MeshElement* elem = *itElem;
5745         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5746         while ( itN->more() ) {
5747           const SMDS_MeshElement* node = itN->next();
5748           if ( newNodes.insert( node ).second )
5749             myBaseP += SMESH_NodeXYZ( node );
5750         }
5751       }
5752     }
5753     myBaseP /= newNodes.size();
5754   }
5755 }
5756
5757 //=======================================================================
5758 //function : ExtrusParam::beginStepIter
5759 //purpose  : prepare iteration on steps
5760 //=======================================================================
5761
5762 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5763 {
5764   myWithMediumNodes = withMediumNodes;
5765   myNextStep = 1;
5766   myCurSteps.clear();
5767 }
5768 //=======================================================================
5769 //function : ExtrusParam::moreSteps
5770 //purpose  : are there more steps?
5771 //=======================================================================
5772
5773 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5774 {
5775   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5776 }
5777 //=======================================================================
5778 //function : ExtrusParam::nextStep
5779 //purpose  : returns the next step
5780 //=======================================================================
5781
5782 double SMESH_MeshEditor::ExtrusParam::nextStep()
5783 {
5784   double res = 0;
5785   if ( !myCurSteps.empty() )
5786   {
5787     res = myCurSteps.back();
5788     myCurSteps.pop_back();
5789   }
5790   else if ( myNextStep <= mySteps->Length() )
5791   {
5792     myCurSteps.push_back( mySteps->Value( myNextStep ));
5793     ++myNextStep;
5794     if ( myWithMediumNodes )
5795     {
5796       myCurSteps.back() /= 2.;
5797       myCurSteps.push_back( myCurSteps.back() );
5798     }
5799     res = nextStep();
5800   }
5801   return res;
5802 }
5803
5804 //=======================================================================
5805 //function : ExtrusParam::makeNodesByDir
5806 //purpose  : create nodes for standard extrusion
5807 //=======================================================================
5808
5809 int SMESH_MeshEditor::ExtrusParam::
5810 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5811                 const SMDS_MeshNode*              srcNode,
5812                 std::list<const SMDS_MeshNode*> & newNodes,
5813                 const bool                        makeMediumNodes)
5814 {
5815   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5816
5817   int nbNodes = 0;
5818   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5819   {
5820     p += myDir.XYZ() * nextStep();
5821     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5822     newNodes.push_back( newNode );
5823   }
5824
5825   if ( !myScales.empty() || !myAngles.empty() )
5826   {
5827     gp_XYZ  center = myBaseP;
5828     gp_Ax1  ratationAxis( center, myDir );
5829     gp_Trsf rotation;
5830
5831     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5832     size_t i = !makeMediumNodes;
5833     for ( beginStepIter( makeMediumNodes );
5834           moreSteps();
5835           ++nIt, i += 1 + !makeMediumNodes )
5836     {
5837       center += myDir.XYZ() * nextStep();
5838
5839       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5840       bool moved = false;
5841       if ( i < myScales.size() )
5842       {
5843         xyz = ( myScales[i] * ( xyz - center )) + center;
5844         moved = true;
5845       }
5846       if ( !myAngles.empty() )
5847       {
5848         rotation.SetRotation( ratationAxis, myAngles[i] );
5849         rotation.Transforms( xyz );
5850         moved = true;
5851       }
5852       if ( moved )
5853         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5854       else
5855         break;
5856     }
5857   }
5858   return nbNodes;
5859 }
5860
5861 //=======================================================================
5862 //function : ExtrusParam::makeNodesByDirAndSew
5863 //purpose  : create nodes for standard extrusion with sewing
5864 //=======================================================================
5865
5866 int SMESH_MeshEditor::ExtrusParam::
5867 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5868                       const SMDS_MeshNode*              srcNode,
5869                       std::list<const SMDS_MeshNode*> & newNodes,
5870                       const bool                        makeMediumNodes)
5871 {
5872   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5873
5874   int nbNodes = 0;
5875   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5876   {
5877     P1 += myDir.XYZ() * nextStep();
5878
5879     // try to search in sequence of existing nodes
5880     // if myNodes.size()>0 we 'nave to use given sequence
5881     // else - use all nodes of mesh
5882     const SMDS_MeshNode * node = 0;
5883     if ( myNodes.Length() > 0 )
5884     {
5885       for ( int i = 1; i <= myNodes.Length(); i++ )
5886       {
5887         SMESH_NodeXYZ P2 = myNodes.Value(i);
5888         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5889         {
5890           node = myNodes.Value(i);
5891           break;
5892         }
5893       }
5894     }
5895     else
5896     {
5897       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5898       while(itn->more())
5899       {
5900         SMESH_NodeXYZ P2 = itn->next();
5901         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5902         {
5903           node = P2._node;
5904           break;
5905         }
5906       }
5907     }
5908
5909     if ( !node )
5910       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5911
5912     newNodes.push_back( node );
5913
5914   } // loop on steps
5915
5916   return nbNodes;
5917 }
5918
5919 //=======================================================================
5920 //function : ExtrusParam::makeNodesByNormal2D
5921 //purpose  : create nodes for extrusion using normals of faces
5922 //=======================================================================
5923
5924 int SMESH_MeshEditor::ExtrusParam::
5925 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5926                      const SMDS_MeshNode*              srcNode,
5927                      std::list<const SMDS_MeshNode*> & newNodes,
5928                      const bool                        makeMediumNodes)
5929 {
5930   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5931
5932   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5933
5934   // get normals to faces sharing srcNode
5935   vector< gp_XYZ > norms, baryCenters;
5936   gp_XYZ norm, avgNorm( 0,0,0 );
5937   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5938   while ( faceIt->more() )
5939   {
5940     const SMDS_MeshElement* face = faceIt->next();
5941     if ( myElemsToUse && !myElemsToUse->count( face ))
5942       continue;
5943     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5944     {
5945       norms.push_back( norm );
5946       avgNorm += norm;
5947       if ( !alongAvgNorm )
5948       {
5949         gp_XYZ bc(0,0,0);
5950         int nbN = 0;
5951         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5952           bc += SMESH_NodeXYZ( nIt->next() );
5953         baryCenters.push_back( bc / nbN );
5954       }
5955     }
5956   }
5957
5958   if ( norms.empty() ) return 0;
5959
5960   double normSize = avgNorm.Modulus();
5961   if ( normSize < std::numeric_limits<double>::min() )
5962     return 0;
5963
5964   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5965   {
5966     myDir = avgNorm;
5967     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5968   }
5969
5970   avgNorm /= normSize;
5971
5972   int nbNodes = 0;
5973   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5974   {
5975     gp_XYZ pNew = p;
5976     double stepSize = nextStep();
5977
5978     if ( norms.size() > 1 )
5979     {
5980       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5981       {
5982         // translate plane of a face
5983         baryCenters[ iF ] += norms[ iF ] * stepSize;
5984
5985         // find point of intersection of the face plane located at baryCenters[ iF ]
5986         // and avgNorm located at pNew
5987         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5988         double dot  = ( norms[ iF ] * avgNorm );
5989         if ( dot < std::numeric_limits<double>::min() )
5990           dot = stepSize * 1e-3;
5991         double step = -( norms[ iF ] * pNew + d ) / dot;
5992         pNew += step * avgNorm;
5993       }
5994     }
5995     else
5996     {
5997       pNew += stepSize * avgNorm;
5998     }
5999     p = pNew;
6000
6001     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6002     newNodes.push_back( newNode );
6003   }
6004   return nbNodes;
6005 }
6006
6007 //=======================================================================
6008 //function : ExtrusParam::makeNodesByNormal1D
6009 //purpose  : create nodes for extrusion using normals of edges
6010 //=======================================================================
6011
6012 int SMESH_MeshEditor::ExtrusParam::
6013 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
6014                      const SMDS_MeshNode*              /*srcNode*/,
6015                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
6016                      const bool                        /*makeMediumNodes*/)
6017 {
6018   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6019   return 0;
6020 }
6021
6022 //=======================================================================
6023 //function : ExtrusParam::makeNodesAlongTrack
6024 //purpose  : create nodes for extrusion along path
6025 //=======================================================================
6026
6027 int SMESH_MeshEditor::ExtrusParam::
6028 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
6029                      const SMDS_MeshNode*              srcNode,
6030                      std::list<const SMDS_MeshNode*> & newNodes,
6031                      const bool                        makeMediumNodes)
6032 {
6033   const Standard_Real aTolAng=1.e-4;
6034
6035   gp_Pnt aV0x = myBaseP;
6036   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6037
6038   const PathPoint& aPP0 = myPathPoints[0];
6039   gp_Pnt aP0x = aPP0.myPnt;
6040   gp_Dir aDT0x= aPP0.myTgt;
6041
6042   std::vector< gp_Pnt > centers;
6043   centers.reserve( NbSteps() * 2 );
6044
6045   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6046
6047   for ( size_t j = 1; j < myPathPoints.size(); ++j )
6048   {
6049     const PathPoint&  aPP  = myPathPoints[j];
6050     const gp_Pnt&     aP1x = aPP.myPnt;
6051     const gp_Dir&    aDT1x = aPP.myTgt;
6052
6053     // Translation
6054     gp_Vec aV01x( aP0x, aP1x );
6055     aTrsf.SetTranslation( aV01x );
6056     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6057     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6058
6059     // rotation 1 [ T1,T0 ]
6060     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6061     if ( fabs( aAngleT1T0 ) > aTolAng )
6062     {
6063       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6064       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6065
6066       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6067     }
6068
6069     // rotation 2
6070     if ( aPP.myAngle != 0. )
6071     {
6072       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6073       aPN1 = aPN1.Transformed( aTrsfRot );
6074     }
6075
6076     // make new node
6077     if ( makeMediumNodes )
6078     {
6079       // create additional node
6080       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6081       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6082       newNodes.push_back( newNode );
6083
6084     }
6085     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6086     newNodes.push_back( newNode );
6087
6088     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6089     centers.push_back( aV1x );
6090
6091     aPN0 = aPN1;
6092     aP0x = aP1x;
6093     aV0x = aV1x;
6094     aDT0x = aDT1x;
6095   }
6096
6097   // scale
6098   if ( !myScales.empty() )
6099   {
6100     gp_Trsf aTrsfScale;
6101     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6102     for ( size_t i = !makeMediumNodes;
6103           i < myScales.size() && node != newNodes.end();
6104           i += ( 1 + !makeMediumNodes ), ++node )
6105     {
6106       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6107       gp_Pnt aN = SMESH_NodeXYZ( *node );
6108       gp_Pnt aP = aN.Transformed( aTrsfScale );
6109       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6110     }
6111   }
6112
6113   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6114 }
6115
6116 //=======================================================================
6117 //function : ExtrusionSweep
6118 //purpose  :
6119 //=======================================================================
6120
6121 SMESH_MeshEditor::PGroupIDs
6122 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
6123                                   const gp_Vec&        theStep,
6124                                   const int            theNbSteps,
6125                                   TTElemOfElemListMap& newElemsMap,
6126                                   const int            theFlags,
6127                                   const double         theTolerance)
6128 {
6129   std::list<double> dummy;
6130   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6131                        theFlags, theTolerance );
6132   return ExtrusionSweep( theElems, aParams, newElemsMap );
6133 }
6134
6135 namespace
6136 {
6137
6138 //=======================================================================
6139 //function : getOriFactor
6140 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
6141 //           edge curve orientation
6142 //=======================================================================
6143
6144   double getOriFactor( const TopoDS_Edge&   edge,
6145                        const SMDS_MeshNode* n1,
6146                        const SMDS_MeshNode* n2,
6147                        SMESH_MesherHelper&  helper)
6148   {
6149     double u1 = helper.GetNodeU( edge, n1, n2 );
6150     double u2 = helper.GetNodeU( edge, n2, n1 );
6151     return u1 < u2 ? 1. : -1.;
6152   }
6153 }
6154
6155 //=======================================================================
6156 //function : ExtrusionSweep
6157 //purpose  :
6158 //=======================================================================
6159
6160 SMESH_MeshEditor::PGroupIDs
6161 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
6162                                   ExtrusParam&         theParams,
6163                                   TTElemOfElemListMap& newElemsMap)
6164 {
6165   ClearLastCreated();
6166
6167   setElemsFirst( theElemSets );
6168   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6169   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6170
6171   // source elements for each generated one
6172   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6173   srcElems.reserve( theElemSets[0].size() );
6174   srcNodes.reserve( theElemSets[1].size() );
6175
6176   const int nbSteps = theParams.NbSteps();
6177   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6178
6179   TNodeOfNodeListMap   mapNewNodes;
6180   TElemOfVecOfNnlmiMap mapElemNewNodes;
6181
6182   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6183                                      myMesh->NbFaces(ORDER_QUADRATIC) +
6184                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
6185   // loop on theElems
6186   TIDSortedElemSet::iterator itElem;
6187   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6188   {
6189     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6190     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6191     {
6192       // check element type
6193       const SMDS_MeshElement* elem = *itElem;
6194       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
6195         continue;
6196
6197       const size_t nbNodes = elem->NbNodes();
6198       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6199       newNodesItVec.reserve( nbNodes );
6200
6201       // loop on elem nodes
6202       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6203       while ( itN->more() )
6204       {
6205         // check if a node has been already sweeped
6206         const SMDS_MeshNode* node = itN->next();
6207         TNodeOfNodeListMap::iterator nIt =
6208           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6209         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6210         if ( listNewNodes.empty() )
6211         {
6212           // make new nodes
6213
6214           // check if we are to create medium nodes between corner ones
6215           bool needMediumNodes = false;
6216           if ( isQuadraticMesh )
6217           {
6218             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6219             while (it->more() && !needMediumNodes )
6220             {
6221               const SMDS_MeshElement* invElem = it->next();
6222               if ( invElem != elem && !theElems.count( invElem )) continue;
6223               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6224               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6225                 needMediumNodes = true;
6226             }
6227           }
6228           // create nodes for all steps
6229           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6230           {
6231             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6232             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6233             {
6234               myLastCreatedNodes.push_back( *newNodesIt );
6235               srcNodes.push_back( node );
6236             }
6237           }
6238           else
6239           {
6240             if ( theParams.ToMakeBoundary() )
6241             {
6242               GetMeshDS()->Modified();
6243               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6244             }
6245             break; // newNodesItVec will be shorter than nbNodes
6246           }
6247         }
6248         newNodesItVec.push_back( nIt );
6249       }
6250       // make new elements
6251       if ( newNodesItVec.size() == nbNodes )
6252         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6253     }
6254   }
6255
6256   if ( theParams.ToMakeBoundary() ) {
6257     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6258   }
6259   PGroupIDs newGroupIDs;
6260   if ( theParams.ToMakeGroups() )
6261     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6262
6263   return newGroupIDs;
6264 }
6265
6266 //=======================================================================
6267 //function : ExtrusionAlongTrack
6268 //purpose  :
6269 //=======================================================================
6270 SMESH_MeshEditor::Extrusion_Error
6271 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6272                                        SMESH_Mesh*          theTrackMesh,
6273                                        SMDS_ElemIteratorPtr theTrackIterator,
6274                                        const SMDS_MeshNode* theN1,
6275                                        std::list<double>&   theAngles,
6276                                        const bool           theAngleVariation,
6277                                        std::list<double>&   theScales,
6278                                        const bool           theScaleVariation,
6279                                        const gp_Pnt*        theRefPoint,
6280                                        const bool           theMakeGroups)
6281 {
6282   ClearLastCreated();
6283
6284   // 1. Check data
6285   if ( theElements[0].empty() && theElements[1].empty() )
6286     return EXTR_NO_ELEMENTS;
6287
6288   ASSERT( theTrackMesh );
6289   if ( ! theTrackIterator || !theTrackIterator->more() )
6290     return EXTR_NO_ELEMENTS;
6291
6292   // 2. Get ordered nodes
6293   SMESH_MeshAlgos::TElemGroupVector branchEdges;
6294   SMESH_MeshAlgos::TNodeGroupVector branchNods;
6295   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6296   if ( branchEdges.empty() )
6297     return EXTR_PATH_NOT_EDGE;
6298
6299   if ( branchEdges.size() > 1 )
6300     return EXTR_BAD_PATH_SHAPE;
6301
6302   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
6303   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6304   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6305     return EXTR_BAD_STARTING_NODE;
6306
6307   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6308   {
6309     // add medium nodes to pathNodes
6310     std::vector< const SMDS_MeshNode* >    pathNodes2;
6311     std::vector< const SMDS_MeshElement* > pathEdges2;
6312     pathNodes2.reserve( pathNodes.size() * 2 );
6313     pathEdges2.reserve( pathEdges.size() * 2 );
6314     for ( size_t i = 0; i < pathEdges.size(); ++i )
6315     {
6316       pathNodes2.push_back( pathNodes[i] );
6317       pathEdges2.push_back( pathEdges[i] );
6318       if ( pathEdges[i]->IsQuadratic() )
6319       {
6320         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6321         pathEdges2.push_back( pathEdges[i] );
6322       }
6323     }
6324     pathNodes2.push_back( pathNodes.back() );
6325     pathEdges.swap( pathEdges2 );
6326     pathNodes.swap( pathNodes2 );
6327   }
6328
6329   // 3. Get path data at pathNodes
6330
6331   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6332
6333   if ( theAngleVariation )
6334     linearAngleVariation( points.size()-1, theAngles );
6335   if ( theScaleVariation )
6336     linearScaleVariation( points.size()-1, theScales );
6337
6338   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6339   std::list<double>::iterator angle = theAngles.begin();
6340
6341   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6342
6343   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6344   std::map< int, double >::iterator id2factor;
6345   SMESH_MesherHelper pathHelper( *theTrackMesh );
6346   gp_Pnt p; gp_Vec tangent;
6347   const double tol2 = gp::Resolution() * gp::Resolution();
6348
6349   for ( size_t i = 0; i < pathNodes.size(); ++i )
6350   {
6351     ExtrusParam::PathPoint & point = points[ i ];
6352
6353     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6354
6355     if ( angle != theAngles.end() )
6356       point.myAngle = *angle++;
6357
6358     tangent.SetCoord( 0,0,0 );
6359     const int          shapeID = pathNodes[ i ]->GetShapeID();
6360     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6361     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6362     switch ( shapeType )
6363     {
6364     case TopAbs_EDGE:
6365     {
6366       TopoDS_Edge edge = TopoDS::Edge( shape );
6367       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6368       if ( id2factor->second == 0 )
6369       {
6370         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6371         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6372       }
6373       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6374       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6375       curve->D1( u, p, tangent );
6376       tangent *= id2factor->second;
6377       break;
6378     }
6379     case TopAbs_VERTEX:
6380     {
6381       int nbEdges = 0;
6382       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6383       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6384       {
6385         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6386         for ( int di = -1; di <= 0; ++di )
6387         {
6388           size_t j = i + di;
6389           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6390           {
6391             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6392             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6393             if ( id2factor->second == 0 )
6394             {
6395               if ( j < i )
6396                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6397               else
6398                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6399             }
6400             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6401             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6402             gp_Vec du;
6403             curve->D1( u, p, du );
6404             double size2 = du.SquareMagnitude();
6405             if ( du.SquareMagnitude() > tol2 )
6406             {
6407               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6408               nbEdges++;
6409             }
6410             break;
6411           }
6412         }
6413       }
6414       if ( nbEdges > 0 )
6415         break;
6416     }
6417     // fall through
6418     default:
6419     {
6420       for ( int di = -1; di <= 1; di += 2 )
6421       {
6422         size_t j = i + di;
6423         if ( j < pathNodes.size() )
6424         {
6425           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6426           double size2 = dir.SquareMagnitude();
6427           if ( size2 > tol2 )
6428             tangent += dir.Divided( Sqrt( size2 )) * di;
6429         }
6430       }
6431     }
6432     } // switch ( shapeType )
6433
6434     if ( tangent.SquareMagnitude() < tol2 )
6435       return EXTR_CANT_GET_TANGENT;
6436
6437     point.myTgt = tangent;
6438
6439   } // loop on pathNodes
6440
6441
6442   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6443   TTElemOfElemListMap newElemsMap;
6444
6445   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6446
6447   return EXTR_OK;
6448 }
6449
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose  : spread values over nbSteps
6453 //=======================================================================
6454
6455 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6456                                             list<double>& Angles)
6457 {
6458   int nbAngles = Angles.size();
6459   if( nbSteps > nbAngles && nbAngles > 0 )
6460   {
6461     vector<double> theAngles(nbAngles);
6462     theAngles.assign( Angles.begin(), Angles.end() );
6463
6464     list<double> res;
6465     double rAn2St = double( nbAngles ) / double( nbSteps );
6466     double angPrev = 0, angle;
6467     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468     {
6469       double angCur = rAn2St * ( iSt+1 );
6470       double angCurFloor  = floor( angCur );
6471       double angPrevFloor = floor( angPrev );
6472       if ( angPrevFloor == angCurFloor )
6473         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474       else {
6475         int iP = int( angPrevFloor );
6476         double angPrevCeil = ceil(angPrev);
6477         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478
6479         int iC = int( angCurFloor );
6480         if ( iC < nbAngles )
6481           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482
6483         iP = int( angPrevCeil );
6484         while ( iC-- > iP )
6485           angle += theAngles[ iC ];
6486       }
6487       res.push_back(angle);
6488       angPrev = angCur;
6489     }
6490     Angles.swap( res );
6491   }
6492 }
6493
6494 //=======================================================================
6495 //function : linearScaleVariation
6496 //purpose  : spread values over nbSteps 
6497 //=======================================================================
6498
6499 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6500                                             std::list<double>& theScales)
6501 {
6502   int nbScales = theScales.size();
6503   std::vector<double> myScales;
6504   myScales.reserve( theNbSteps );
6505   std::list<double>::const_iterator scale = theScales.begin();
6506   double prevScale = 1.0;
6507   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6508   {
6509     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6510     int    stDelta = Max( 1, iStep - myScales.size());
6511     double scDelta = ( *scale - prevScale ) / stDelta;
6512     for ( int iStep = 0; iStep < stDelta; ++iStep )
6513     {
6514       myScales.push_back( prevScale + scDelta );
6515       prevScale = myScales.back();
6516     }
6517     prevScale = *scale;
6518   }
6519   theScales.assign( myScales.begin(), myScales.end() );
6520 }
6521
6522 //================================================================================
6523 /*!
6524  * \brief Move or copy theElements applying theTrsf to their nodes
6525  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6526  *  \param theTrsf - transformation to apply
6527  *  \param theCopy - if true, create translated copies of theElems
6528  *  \param theMakeGroups - if true and theCopy, create translated groups
6529  *  \param theTargetMesh - mesh to copy translated elements into
6530  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6531  */
6532 //================================================================================
6533
6534 SMESH_MeshEditor::PGroupIDs
6535 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6536                              const gp_Trsf&     theTrsf,
6537                              const bool         theCopy,
6538                              const bool         theMakeGroups,
6539                              SMESH_Mesh*        theTargetMesh)
6540 {
6541   ClearLastCreated();
6542   myLastCreatedElems.reserve( theElems.size() );
6543
6544   bool needReverse = false;
6545   string groupPostfix;
6546   switch ( theTrsf.Form() ) {
6547   case gp_PntMirror:
6548     needReverse = true;
6549     groupPostfix = "mirrored";
6550     break;
6551   case gp_Ax1Mirror:
6552     groupPostfix = "mirrored";
6553     break;
6554   case gp_Ax2Mirror:
6555     needReverse = true;
6556     groupPostfix = "mirrored";
6557     break;
6558   case gp_Rotation:
6559     groupPostfix = "rotated";
6560     break;
6561   case gp_Translation:
6562     groupPostfix = "translated";
6563     break;
6564   case gp_Scale:
6565     groupPostfix = "scaled";
6566     break;
6567   case gp_CompoundTrsf: // different scale by axis
6568     groupPostfix = "scaled";
6569     break;
6570   default:
6571     needReverse = false;
6572     groupPostfix = "transformed";
6573   }
6574
6575   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6576   SMESHDS_Mesh* aMesh    = GetMeshDS();
6577
6578   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6579   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6580   SMESH_MeshEditor::ElemFeatures elemType;
6581
6582   // map old node to new one
6583   TNodeNodeMap nodeMap;
6584
6585   // elements sharing moved nodes; those of them which have all
6586   // nodes mirrored but are not in theElems are to be reversed
6587   TIDSortedElemSet inverseElemSet;
6588
6589   // source elements for each generated one
6590   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6591
6592   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6593   TIDSortedElemSet orphanNode;
6594
6595   if ( theElems.empty() ) // transform the whole mesh
6596   {
6597     // add all elements
6598     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6599     while ( eIt->more() ) theElems.insert( eIt->next() );
6600     // add orphan nodes
6601     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6602     while ( nIt->more() )
6603     {
6604       const SMDS_MeshNode* node = nIt->next();
6605       if ( node->NbInverseElements() == 0)
6606         orphanNode.insert( node );
6607     }
6608   }
6609
6610   // loop on elements to transform nodes : first orphan nodes then elems
6611   TIDSortedElemSet::iterator itElem;
6612   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6613   for (int i=0; i<2; i++)
6614     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6615     {
6616       const SMDS_MeshElement* elem = *itElem;
6617       if ( !elem )
6618         continue;
6619
6620       // loop on elem nodes
6621       double coord[3];
6622       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6623       while ( itN->more() )
6624       {
6625         const SMDS_MeshNode* node = cast2Node( itN->next() );
6626         // check if a node has been already transformed
6627         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6628           nodeMap.insert( make_pair ( node, node ));
6629         if ( !n2n_isnew.second )
6630           continue;
6631
6632         node->GetXYZ( coord );
6633         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6634         if ( theTargetMesh ) {
6635           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6636           n2n_isnew.first->second = newNode;
6637           myLastCreatedNodes.push_back(newNode);
6638           srcNodes.push_back( node );
6639         }
6640         else if ( theCopy ) {
6641           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6642           n2n_isnew.first->second = newNode;
6643           myLastCreatedNodes.push_back(newNode);
6644           srcNodes.push_back( node );
6645         }
6646         else {
6647           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6648           // node position on shape becomes invalid
6649           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6650             ( SMDS_SpacePosition::originSpacePosition() );
6651         }
6652
6653         // keep inverse elements
6654         if ( !theCopy && !theTargetMesh && needReverse ) {
6655           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6656           while ( invElemIt->more() ) {
6657             const SMDS_MeshElement* iel = invElemIt->next();
6658             inverseElemSet.insert( iel );
6659           }
6660         }
6661       }
6662     } // loop on elems in { &orphanNode, &theElems };
6663
6664   // either create new elements or reverse mirrored ones
6665   if ( !theCopy && !needReverse && !theTargetMesh )
6666     return PGroupIDs();
6667
6668   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6669
6670   // Replicate or reverse elements
6671
6672   std::vector<int> iForw;
6673   vector<const SMDS_MeshNode*> nodes;
6674   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6675   {
6676     const SMDS_MeshElement* elem = *itElem;
6677     if ( !elem ) continue;
6678
6679     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6680     size_t               nbNodes  = elem->NbNodes();
6681     if ( geomType == SMDSGeom_NONE ) continue; // node
6682
6683     nodes.resize( nbNodes );
6684
6685     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6686     {
6687       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6688       if ( !aPolyedre )
6689         continue;
6690       nodes.clear();
6691       bool allTransformed = true;
6692       int nbFaces = aPolyedre->NbFaces();
6693       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6694       {
6695         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6696         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6697         {
6698           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6699           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6700           if ( nodeMapIt == nodeMap.end() )
6701             allTransformed = false; // not all nodes transformed
6702           else
6703             nodes.push_back((*nodeMapIt).second);
6704         }
6705         if ( needReverse && allTransformed )
6706           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6707       }
6708       if ( !allTransformed )
6709         continue; // not all nodes transformed
6710     }
6711     else // ----------------------- the rest element types
6712     {
6713       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6714       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6715       const vector<int>&    i = needReverse ? iRev : iForw;
6716
6717       // find transformed nodes
6718       size_t iNode = 0;
6719       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6720       while ( itN->more() ) {
6721         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6722         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6723         if ( nodeMapIt == nodeMap.end() )
6724           break; // not all nodes transformed
6725         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6726       }
6727       if ( iNode != nbNodes )
6728         continue; // not all nodes transformed
6729     }
6730
6731     if ( editor ) {
6732       // copy in this or a new mesh
6733       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6734         srcElems.push_back( elem );
6735     }
6736     else {
6737       // reverse element as it was reversed by transformation
6738       if ( nbNodes > 2 )
6739         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6740     }
6741
6742   } // loop on elements
6743
6744   if ( editor && editor != this )
6745     myLastCreatedElems.swap( editor->myLastCreatedElems );
6746
6747   PGroupIDs newGroupIDs;
6748
6749   if ( ( theMakeGroups && theCopy ) ||
6750        ( theMakeGroups && theTargetMesh ) )
6751     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6752
6753   return newGroupIDs;
6754 }
6755
6756 //================================================================================
6757 /*!
6758  * \brief Make an offset mesh from a source 2D mesh
6759  *  \param [in] theElements - source faces
6760  *  \param [in] theValue - offset value
6761  *  \param [out] theTgtMesh - a mesh to add offset elements to
6762  *  \param [in] theMakeGroups - to generate groups
6763  *  \return PGroupIDs - IDs of created groups. NULL means failure
6764  */
6765 //================================================================================
6766
6767 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6768                                                       const double       theValue,
6769                                                       SMESH_Mesh*        theTgtMesh,
6770                                                       const bool         theMakeGroups,
6771                                                       const bool         theCopyElements,
6772                                                       const bool         theFixSelfIntersection)
6773 {
6774   SMESHDS_Mesh*    meshDS = GetMeshDS();
6775   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6776   SMESH_MeshEditor tgtEditor( theTgtMesh );
6777
6778   SMDS_ElemIteratorPtr eIt;
6779   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6780   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6781
6782   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6783   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6784   std::unique_ptr< SMDS_Mesh > offsetMesh
6785     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6786                                    theFixSelfIntersection,
6787                                    new2OldFaces, new2OldNodes ));
6788   if ( offsetMesh->NbElements() == 0 )
6789     return PGroupIDs(); // MakeOffset() failed
6790
6791
6792   if ( theTgtMesh == myMesh && !theCopyElements )
6793   {
6794     // clear the source elements
6795     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6796     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6797     while ( eIt->more() )
6798       meshDS->RemoveFreeElement( eIt->next(), 0 );
6799   }
6800
6801   // offsetMesh->Modified();
6802   // offsetMesh->CompactMesh(); // make IDs start from 1
6803
6804   // source elements for each generated one
6805   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6806   srcElems.reserve( new2OldFaces.size() );
6807   srcNodes.reserve( new2OldNodes.size() );
6808
6809   ClearLastCreated();
6810   myLastCreatedElems.reserve( new2OldFaces.size() );
6811   myLastCreatedNodes.reserve( new2OldNodes.size() );
6812
6813   // copy offsetMesh to theTgtMesh
6814
6815   smIdType idShift = meshDS->MaxNodeID();
6816   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6817     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6818     {
6819 #ifndef _DEBUG_
6820       if ( n->NbInverseElements() > 0 )
6821 #endif
6822       {
6823         const SMDS_MeshNode* n2 =
6824           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6825         myLastCreatedNodes.push_back( n2 );
6826         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6827       }
6828     }
6829
6830   ElemFeatures elemType;
6831   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6832     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6833     {
6834       elemType.Init( f );
6835       elemType.myNodes.clear();
6836       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6837       {
6838         const SMDS_MeshNode* n2 = nIt->next();
6839         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6840       }
6841       tgtEditor.AddElement( elemType.myNodes, elemType );
6842       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6843     }
6844
6845   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6846
6847   PGroupIDs newGroupIDs;
6848   if ( theMakeGroups )
6849     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6850   else
6851     newGroupIDs.reset( new std::list< int > );
6852
6853   return newGroupIDs;
6854 }
6855
6856 //=======================================================================
6857 /*!
6858  * \brief Create groups of elements made during transformation
6859  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6860  *  \param elemGens - elements making corresponding myLastCreatedElems
6861  *  \param postfix - to push_back to names of new groups
6862  *  \param targetMesh - mesh to create groups in
6863  *  \param topPresent - is there are "top" elements that are created by sweeping
6864  */
6865 //=======================================================================
6866
6867 SMESH_MeshEditor::PGroupIDs
6868 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6869                                  const SMESH_SequenceOfElemPtr& elemGens,
6870                                  const std::string&             postfix,
6871                                  SMESH_Mesh*                    targetMesh,
6872                                  const bool                     topPresent)
6873 {
6874   PGroupIDs newGroupIDs( new list<int> );
6875   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6876
6877   // Sort existing groups by types and collect their names
6878
6879   // containers to store an old group and generated new ones;
6880   // 1st new group is for result elems of different type than a source one;
6881   // 2nd new group is for same type result elems ("top" group at extrusion)
6882   using boost::tuple;
6883   using boost::make_tuple;
6884   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6885   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6886   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6887   // group names
6888   set< string > groupNames;
6889
6890   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6891   if ( !groupIt->more() ) return newGroupIDs;
6892
6893   int newGroupID = mesh->GetGroupIds().back()+1;
6894   while ( groupIt->more() )
6895   {
6896     SMESH_Group * group = groupIt->next();
6897     if ( !group ) continue;
6898     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6899     if ( !groupDS || groupDS->IsEmpty() ) continue;
6900     groupNames.insert    ( group->GetName() );
6901     groupDS->SetStoreName( group->GetName() );
6902     const SMDSAbs_ElementType type = groupDS->GetType();
6903     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6904     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6905     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6906     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6907   }
6908
6909   // Loop on nodes and elements to add them in new groups
6910
6911   vector< const SMDS_MeshElement* > resultElems;
6912   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6913   {
6914     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6915     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6916     if ( gens.size() != elems.size() )
6917       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6918
6919     // loop on created elements
6920     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6921     {
6922       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6923       if ( !sourceElem ) {
6924         MESSAGE("generateGroups(): NULL source element");
6925         continue;
6926       }
6927       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6928       if ( groupsOldNew.empty() ) { // no groups of this type at all
6929         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6930           ++iElem; // skip all elements made by sourceElem
6931         continue;
6932       }
6933       // collect all elements made by the iElem-th sourceElem
6934       resultElems.clear();
6935       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6936         if ( resElem != sourceElem )
6937           resultElems.push_back( resElem );
6938       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6939         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6940           if ( resElem != sourceElem )
6941             resultElems.push_back( resElem );
6942
6943       const SMDS_MeshElement* topElem = 0;
6944       if ( isNodes ) // there must be a top element
6945       {
6946         topElem = resultElems.back();
6947         resultElems.pop_back();
6948       }
6949       else
6950       {
6951         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6952         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6953           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6954           {
6955             topElem = *resElemIt;
6956             *resElemIt = 0; // erase *resElemIt
6957             break;
6958           }
6959       }
6960       // add resultElems to groups originted from ones the sourceElem belongs to
6961       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6962       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6963       {
6964         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6965         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6966         {
6967           // fill in a new group
6968           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6969           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6970           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6971             if ( *resElemIt )
6972               newGroup.Add( *resElemIt );
6973
6974           // fill a "top" group
6975           if ( topElem )
6976           {
6977             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6978             newTopGroup.Add( topElem );
6979           }
6980         }
6981       }
6982     } // loop on created elements
6983   }// loop on nodes and elements
6984
6985   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6986
6987   list<int> topGrouIds;
6988   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6989   {
6990     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6991     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6992                                       orderedOldNewGroups[i]->get<2>() };
6993     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6994     {
6995       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6996       if ( newGroupDS->IsEmpty() )
6997       {
6998         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6999       }
7000       else
7001       {
7002         // set group type
7003         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7004
7005         // make a name
7006         const bool isTop = ( topPresent &&
7007                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7008                              is2nd );
7009
7010         string name = oldGroupDS->GetStoreName();
7011         { // remove trailing whitespaces (issue 22599)
7012           size_t size = name.size();
7013           while ( size > 1 && isspace( name[ size-1 ]))
7014             --size;
7015           if ( size != name.size() )
7016           {
7017             name.resize( size );
7018             oldGroupDS->SetStoreName( name.c_str() );
7019           }
7020         }
7021         if ( !targetMesh ) {
7022           string suffix = ( isTop ? "top": postfix.c_str() );
7023           name += "_";
7024           name += suffix;
7025           int nb = 1;
7026           while ( !groupNames.insert( name ).second ) // name exists
7027             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7028         }
7029         else if ( isTop ) {
7030           name += "_top";
7031         }
7032         newGroupDS->SetStoreName( name.c_str() );
7033
7034         // make a SMESH_Groups
7035         mesh->AddGroup( newGroupDS );
7036         if ( isTop )
7037           topGrouIds.push_back( newGroupDS->GetID() );
7038         else
7039           newGroupIDs->push_back( newGroupDS->GetID() );
7040       }
7041     }
7042   }
7043   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7044
7045   return newGroupIDs;
7046 }
7047
7048 //================================================================================
7049 /*!
7050  *  * \brief Return list of group of nodes close to each other within theTolerance
7051  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7052  *  *        an Octree algorithm
7053  *  \param [in,out] theNodes - the nodes to treat
7054  *  \param [in]     theTolerance - the tolerance
7055  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7056  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7057  *         corner and medium nodes in separate groups
7058  */
7059 //================================================================================
7060
7061 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7062                                             const double         theTolerance,
7063                                             TListOfListOfNodes & theGroupsOfNodes,
7064                                             bool                 theSeparateCornersAndMedium)
7065 {
7066   ClearLastCreated();
7067
7068   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7069        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7070        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7071     theSeparateCornersAndMedium = false;
7072
7073   TIDSortedNodeSet& corners = theNodes;
7074   TIDSortedNodeSet  medium;
7075
7076   if ( theNodes.empty() ) // get all nodes in the mesh
7077   {
7078     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7079     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7080     if ( theSeparateCornersAndMedium )
7081       while ( nIt->more() )
7082       {
7083         const SMDS_MeshNode* n = nIt->next();
7084         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7085         nodeSet->insert( nodeSet->end(), n );
7086       }
7087     else
7088       while ( nIt->more() )
7089         theNodes.insert( theNodes.end(), nIt->next() );
7090   }
7091   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7092   {
7093     TIDSortedNodeSet::iterator nIt = corners.begin();
7094     while ( nIt != corners.end() )
7095       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7096       {
7097         medium.insert( medium.end(), *nIt );
7098         corners.erase( nIt++ );
7099       }
7100       else
7101       {
7102         ++nIt;
7103       }
7104   }
7105
7106   if ( !corners.empty() )
7107     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7108   if ( !medium.empty() )
7109     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7110 }
7111
7112 //=======================================================================
7113 //function : SimplifyFace
7114 //purpose  : split a chain of nodes into several closed chains
7115 //=======================================================================
7116
7117 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7118                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7119                                     vector<int>&                         quantities) const
7120 {
7121   int nbNodes = faceNodes.size();
7122   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7123     --nbNodes;
7124   if ( nbNodes < 3 )
7125     return 0;
7126   size_t prevNbQuant = quantities.size();
7127
7128   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7129   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7130   map< const SMDS_MeshNode*, int >::iterator nInd;
7131
7132   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7133   simpleNodes.push_back( faceNodes[0] );
7134   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7135   {
7136     if ( faceNodes[ iCur ] != simpleNodes.back() )
7137     {
7138       int index = simpleNodes.size();
7139       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7140       int prevIndex = nInd->second;
7141       if ( prevIndex < index )
7142       {
7143         // a sub-loop found
7144         int loopLen = index - prevIndex;
7145         if ( loopLen > 2 )
7146         {
7147           // store the sub-loop
7148           quantities.push_back( loopLen );
7149           for ( int i = prevIndex; i < index; i++ )
7150             poly_nodes.push_back( simpleNodes[ i ]);
7151         }
7152         simpleNodes.resize( prevIndex+1 );
7153       }
7154       else
7155       {
7156         simpleNodes.push_back( faceNodes[ iCur ]);
7157       }
7158     }
7159   }
7160
7161   if ( simpleNodes.size() > 2 )
7162   {
7163     quantities.push_back( simpleNodes.size() );
7164     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7165   }
7166
7167   return quantities.size() - prevNbQuant;
7168 }
7169
7170 //=======================================================================
7171 //function : MergeNodes
7172 //purpose  : In each group, the cdr of nodes are substituted by the first one
7173 //           in all elements.
7174 //=======================================================================
7175
7176 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7177                                    const bool           theAvoidMakingHoles)
7178 {
7179   ClearLastCreated();
7180
7181   SMESHDS_Mesh* mesh = GetMeshDS();
7182
7183   TNodeNodeMap nodeNodeMap; // node to replace - new node
7184   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7185   list< smIdType > rmElemIds, rmNodeIds;
7186   vector< ElemFeatures > newElemDefs;
7187
7188   // Fill nodeNodeMap and elems
7189
7190   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7191   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7192   {
7193     list<const SMDS_MeshNode*>& nodes = *grIt;
7194     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7195     const SMDS_MeshNode* nToKeep = *nIt;
7196     for ( ++nIt; nIt != nodes.end(); nIt++ )
7197     {
7198       const SMDS_MeshNode* nToRemove = *nIt;
7199       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7200       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7201       while ( invElemIt->more() ) {
7202         const SMDS_MeshElement* elem = invElemIt->next();
7203         elems.insert(elem);
7204       }
7205     }
7206   }
7207
7208   // Apply recursive replacements (BUG 0020185)
7209   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7210   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7211   {
7212     const SMDS_MeshNode* nToKeep = nnIt->second;
7213     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7214     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7215     {
7216       nToKeep = nnIt_i->second;
7217       nnIt->second = nToKeep;
7218       nnIt_i = nodeNodeMap.find( nToKeep );
7219     }
7220   }
7221
7222   if ( theAvoidMakingHoles )
7223   {
7224     // find elements whose topology changes
7225
7226     vector<const SMDS_MeshElement*> pbElems;
7227     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7228     for ( ; eIt != elems.end(); ++eIt )
7229     {
7230       const SMDS_MeshElement* elem = *eIt;
7231       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7232       while ( itN->more() )
7233       {
7234         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7235         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7236         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7237         {
7238           // several nodes of elem stick
7239           pbElems.push_back( elem );
7240           break;
7241         }
7242       }
7243     }
7244     // exclude from merge nodes causing spoiling element
7245     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7246     {
7247       bool nodesExcluded = false;
7248       for ( size_t i = 0; i < pbElems.size(); ++i )
7249       {
7250         size_t prevNbMergeNodes = nodeNodeMap.size();
7251         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7252              prevNbMergeNodes < nodeNodeMap.size() )
7253           nodesExcluded = true;
7254       }
7255       if ( !nodesExcluded )
7256         break;
7257     }
7258   }
7259
7260   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7261   {
7262     const SMDS_MeshNode* nToRemove = nnIt->first;
7263     const SMDS_MeshNode* nToKeep   = nnIt->second;
7264     if ( nToRemove != nToKeep )
7265     {
7266       rmNodeIds.push_back( nToRemove->GetID() );
7267       AddToSameGroups( nToKeep, nToRemove, mesh );
7268       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7269       // w/o creating node in place of merged ones.
7270       SMDS_PositionPtr pos = nToRemove->GetPosition();
7271       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7272         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7273           sm->SetIsAlwaysComputed( true );
7274     }
7275   }
7276
7277   // Change element nodes or remove an element
7278
7279   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7280   for ( ; eIt != elems.end(); eIt++ )
7281   {
7282     const SMDS_MeshElement* elem = *eIt;
7283     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7284
7285     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7286     if ( !keepElem )
7287       rmElemIds.push_back( elem->GetID() );
7288
7289     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7290     {
7291       bool elemChanged = false;
7292       if ( i == 0 )
7293       {
7294         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7295           elemChanged = mesh->ChangePolyhedronNodes( elem,
7296                                                      newElemDefs[i].myNodes,
7297                                                      newElemDefs[i].myPolyhedQuantities );
7298         else
7299           elemChanged = mesh->ChangeElementNodes( elem,
7300                                                   & newElemDefs[i].myNodes[0],
7301                                                   newElemDefs[i].myNodes.size() );
7302       }
7303       if ( i > 0 || !elemChanged )
7304       {
7305         if ( i == 0 )
7306         {
7307           newElemDefs[i].SetID( elem->GetID() );
7308           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7309           if ( !keepElem ) rmElemIds.pop_back();
7310         }
7311         else
7312         {
7313           newElemDefs[i].SetID( -1 );
7314         }
7315         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7316         if ( sm && newElem )
7317           sm->AddElement( newElem );
7318         if ( elem != newElem )
7319           ReplaceElemInGroups( elem, newElem, mesh );
7320       }
7321     }
7322   }
7323
7324   // Remove bad elements, then equal nodes (order important)
7325   Remove( rmElemIds, /*isNodes=*/false );
7326   Remove( rmNodeIds, /*isNodes=*/true );
7327
7328   return;
7329 }
7330
7331 //=======================================================================
7332 //function : applyMerge
7333 //purpose  : Compute new connectivity of an element after merging nodes
7334 //  \param [in] elems - the element
7335 //  \param [out] newElemDefs - definition(s) of result element(s)
7336 //  \param [inout] nodeNodeMap - nodes to merge
7337 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7338 //              after merging (but not degenerated), removes nodes causing
7339 //              the invalidity from \a nodeNodeMap.
7340 //  \return bool - true if the element should be removed
7341 //=======================================================================
7342
7343 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7344                                    vector< ElemFeatures >& newElemDefs,
7345                                    TNodeNodeMap&           nodeNodeMap,
7346                                    const bool              avoidMakingHoles )
7347 {
7348   bool toRemove = false; // to remove elem
7349   int nbResElems = 1;    // nb new elements
7350
7351   newElemDefs.resize(nbResElems);
7352   newElemDefs[0].Init( elem );
7353   newElemDefs[0].myNodes.clear();
7354
7355   set<const SMDS_MeshNode*> nodeSet;
7356   vector< const SMDS_MeshNode*>   curNodes;
7357   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7358   vector<int> iRepl;
7359
7360   const        int  nbNodes = elem->NbNodes();
7361   SMDSAbs_EntityType entity = elem->GetEntityType();
7362
7363   curNodes.resize( nbNodes );
7364   uniqueNodes.resize( nbNodes );
7365   iRepl.resize( nbNodes );
7366   int iUnique = 0, iCur = 0, nbRepl = 0;
7367
7368   // Get new seq of nodes
7369
7370   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7371   while ( itN->more() )
7372   {
7373     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7374
7375     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7376     if ( nnIt != nodeNodeMap.end() ) {
7377       n = (*nnIt).second;
7378     }
7379     curNodes[ iCur ] = n;
7380     bool isUnique = nodeSet.insert( n ).second;
7381     if ( isUnique )
7382       uniqueNodes[ iUnique++ ] = n;
7383     else
7384       iRepl[ nbRepl++ ] = iCur;
7385     iCur++;
7386   }
7387
7388   // Analyse element topology after replacement
7389
7390   int nbUniqueNodes = nodeSet.size();
7391   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7392   {
7393     toRemove = true;
7394     nbResElems = 0;
7395
7396     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7397     {
7398       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7399       int nbCorners = nbNodes / 2;
7400       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7401       {
7402         int iNext = ( iCur + 1 ) % nbCorners;
7403         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7404         {
7405           int iMedium = iCur + nbCorners;
7406           vector< const SMDS_MeshNode* >::iterator i =
7407             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7408                        uniqueNodes.end(),
7409                        curNodes[ iMedium ]);
7410           if ( i != uniqueNodes.end() )
7411           {
7412             --nbUniqueNodes;
7413             for ( ; i+1 != uniqueNodes.end(); ++i )
7414               *i = *(i+1);
7415           }
7416         }
7417       }
7418     }
7419
7420     switch ( entity )
7421     {
7422     case SMDSEntity_Polygon:
7423     case SMDSEntity_Quad_Polygon: // Polygon
7424     {
7425       ElemFeatures* elemType = & newElemDefs[0];
7426       const bool isQuad = elemType->myIsQuad;
7427       if ( isQuad )
7428         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7429           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7430
7431       // a polygon can divide into several elements
7432       vector<const SMDS_MeshNode *> polygons_nodes;
7433       vector<int> quantities;
7434       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7435       newElemDefs.resize( nbResElems );
7436       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7437       {
7438         ElemFeatures* elemType = & newElemDefs[iface];
7439         if ( iface ) elemType->Init( elem );
7440
7441         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7442         int nbNewNodes = quantities[iface];
7443         face_nodes.assign( polygons_nodes.begin() + inode,
7444                            polygons_nodes.begin() + inode + nbNewNodes );
7445         inode += nbNewNodes;
7446         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7447         {
7448           bool isValid = ( nbNewNodes % 2 == 0 );
7449           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7450             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7451           elemType->SetQuad( isValid );
7452           if ( isValid ) // put medium nodes after corners
7453             SMDS_MeshCell::applyInterlaceRev
7454               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7455                                                     nbNewNodes ), face_nodes );
7456         }
7457         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7458       }
7459       nbUniqueNodes = newElemDefs[0].myNodes.size();
7460       break;
7461     } // Polygon
7462
7463     case SMDSEntity_Polyhedra: // Polyhedral volume
7464     {
7465       if ( nbUniqueNodes >= 4 )
7466       {
7467         // each face has to be analyzed in order to check volume validity
7468         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7469         {
7470           toRemove = false;
7471           int nbFaces = aPolyedre->NbFaces();
7472
7473           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7474           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7475           vector<const SMDS_MeshNode *>  faceNodes;
7476           poly_nodes.clear();
7477           quantities.clear();
7478
7479           for (int iface = 1; iface <= nbFaces; iface++)
7480           {
7481             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7482             faceNodes.resize( nbFaceNodes );
7483             for (int inode = 1; inode <= nbFaceNodes; inode++)
7484             {
7485               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7486               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7487               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7488                 faceNode = (*nnIt).second;
7489               faceNodes[inode - 1] = faceNode;
7490             }
7491             SimplifyFace(faceNodes, poly_nodes, quantities);
7492           }
7493
7494           if ( quantities.size() > 3 )
7495           {
7496             // TODO: remove coincident faces
7497             nbResElems = 1;
7498             nbUniqueNodes = newElemDefs[0].myNodes.size();
7499           }
7500         }
7501       }
7502     }
7503     break;
7504
7505     // Regular elements
7506     // TODO not all the possible cases are solved. Find something more generic?
7507     case SMDSEntity_Edge: //////// EDGE
7508     case SMDSEntity_Triangle: //// TRIANGLE
7509     case SMDSEntity_Quad_Triangle:
7510     case SMDSEntity_Tetra:
7511     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7512     {
7513       break;
7514     }
7515     case SMDSEntity_Quad_Edge:
7516     {
7517       break;
7518     }
7519     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7520     {
7521       if ( nbUniqueNodes < 3 )
7522         toRemove = true;
7523       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7524         toRemove = true; // opposite nodes stick
7525       else
7526         toRemove = false;
7527       break;
7528     }
7529     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7530     {
7531       //   1    5    2
7532       //    +---+---+
7533       //    |       |
7534       //   4+       +6
7535       //    |       |
7536       //    +---+---+
7537       //   0    7    3
7538       if ( nbUniqueNodes == 6 &&
7539            iRepl[0] < 4       &&
7540            ( nbRepl == 1 || iRepl[1] >= 4 ))
7541       {
7542         toRemove = false;
7543       }
7544       break;
7545     }
7546     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7547     {
7548       //   1    5    2
7549       //    +---+---+
7550       //    |       |
7551       //   4+  8+   +6
7552       //    |       |
7553       //    +---+---+
7554       //   0    7    3
7555       if ( nbUniqueNodes == 7 &&
7556            iRepl[0] < 4       &&
7557            ( nbRepl == 1 || iRepl[1] != 8 ))
7558       {
7559         toRemove = false;
7560       }
7561       break;
7562     }
7563     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7564     {
7565       if ( nbUniqueNodes == 4 ) {
7566         // ---------------------------------> tetrahedron
7567         if ( curNodes[3] == curNodes[4] &&
7568              curNodes[3] == curNodes[5] ) {
7569           // top nodes stick
7570           toRemove = false;
7571         }
7572         else if ( curNodes[0] == curNodes[1] &&
7573                   curNodes[0] == curNodes[2] ) {
7574           // bottom nodes stick: set a top before
7575           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7576           uniqueNodes[ 0 ] = curNodes [ 5 ];
7577           uniqueNodes[ 1 ] = curNodes [ 4 ];
7578           uniqueNodes[ 2 ] = curNodes [ 3 ];
7579           toRemove = false;
7580         }
7581         else if (( curNodes[0] == curNodes[3] ) +
7582                  ( curNodes[1] == curNodes[4] ) +
7583                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7584           // a lateral face turns into a line
7585           toRemove = false;
7586         }
7587       }
7588       else if ( nbUniqueNodes == 5 ) {
7589         // PENTAHEDRON --------------------> pyramid
7590         if ( curNodes[0] == curNodes[3] )
7591         {
7592           uniqueNodes[ 0 ] = curNodes[ 1 ];
7593           uniqueNodes[ 1 ] = curNodes[ 4 ];
7594           uniqueNodes[ 2 ] = curNodes[ 5 ];
7595           uniqueNodes[ 3 ] = curNodes[ 2 ];
7596           uniqueNodes[ 4 ] = curNodes[ 0 ];
7597           toRemove = false;
7598         }
7599         if ( curNodes[1] == curNodes[4] )
7600         {
7601           uniqueNodes[ 0 ] = curNodes[ 0 ];
7602           uniqueNodes[ 1 ] = curNodes[ 2 ];
7603           uniqueNodes[ 2 ] = curNodes[ 5 ];
7604           uniqueNodes[ 3 ] = curNodes[ 3 ];
7605           uniqueNodes[ 4 ] = curNodes[ 1 ];
7606           toRemove = false;
7607         }
7608         if ( curNodes[2] == curNodes[5] )
7609         {
7610           uniqueNodes[ 0 ] = curNodes[ 0 ];
7611           uniqueNodes[ 1 ] = curNodes[ 3 ];
7612           uniqueNodes[ 2 ] = curNodes[ 4 ];
7613           uniqueNodes[ 3 ] = curNodes[ 1 ];
7614           uniqueNodes[ 4 ] = curNodes[ 2 ];
7615           toRemove = false;
7616         }
7617       }
7618       break;
7619     }
7620     case SMDSEntity_Hexa:
7621     {
7622       //////////////////////////////////// HEXAHEDRON
7623       SMDS_VolumeTool hexa (elem);
7624       hexa.SetExternalNormal();
7625       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7626         //////////////////////// HEX ---> tetrahedron
7627         for ( int iFace = 0; iFace < 6; iFace++ ) {
7628           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7629           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7630               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7631               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7632             // one face turns into a point ...
7633             int  pickInd = ind[ 0 ];
7634             int iOppFace = hexa.GetOppFaceIndex( iFace );
7635             ind = hexa.GetFaceNodesIndices( iOppFace );
7636             int nbStick = 0;
7637             uniqueNodes.clear();
7638             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7639               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7640                 nbStick++;
7641               else
7642                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7643             }
7644             if ( nbStick == 1 ) {
7645               // ... and the opposite one - into a triangle.
7646               // set a top node
7647               uniqueNodes.push_back( curNodes[ pickInd ]);
7648               toRemove = false;
7649             }
7650             break;
7651           }
7652         }
7653       }
7654       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7655         //////////////////////// HEX ---> prism
7656         int nbTria = 0, iTria[3];
7657         const int *ind; // indices of face nodes
7658         // look for triangular faces
7659         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7660           ind = hexa.GetFaceNodesIndices( iFace );
7661           TIDSortedNodeSet faceNodes;
7662           for ( iCur = 0; iCur < 4; iCur++ )
7663             faceNodes.insert( curNodes[ind[iCur]] );
7664           if ( faceNodes.size() == 3 )
7665             iTria[ nbTria++ ] = iFace;
7666         }
7667         // check if triangles are opposite
7668         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7669         {
7670           // set nodes of the bottom triangle
7671           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7672           vector<int> indB;
7673           for ( iCur = 0; iCur < 4; iCur++ )
7674             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7675               indB.push_back( ind[iCur] );
7676           if ( !hexa.IsForward() )
7677             std::swap( indB[0], indB[2] );
7678           for ( iCur = 0; iCur < 3; iCur++ )
7679             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7680           // set nodes of the top triangle
7681           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7682           for ( iCur = 0; iCur < 3; ++iCur )
7683             for ( int j = 0; j < 4; ++j )
7684               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7685               {
7686                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7687                 break;
7688               }
7689           toRemove = false;
7690           break;
7691         }
7692       }
7693       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7694         //////////////////// HEXAHEDRON ---> pyramid
7695         for ( int iFace = 0; iFace < 6; iFace++ ) {
7696           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7697           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7698               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7699               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7700             // one face turns into a point ...
7701             int iOppFace = hexa.GetOppFaceIndex( iFace );
7702             ind = hexa.GetFaceNodesIndices( iOppFace );
7703             uniqueNodes.clear();
7704             for ( iCur = 0; iCur < 4; iCur++ ) {
7705               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7706                 break;
7707               else
7708                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7709             }
7710             if ( uniqueNodes.size() == 4 ) {
7711               // ... and the opposite one is a quadrangle
7712               // set a top node
7713               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7714               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7715               toRemove = false;
7716             }
7717             break;
7718           }
7719         }
7720       }
7721
7722       if ( toRemove && nbUniqueNodes > 4 ) {
7723         ////////////////// HEXAHEDRON ---> polyhedron
7724         hexa.SetExternalNormal();
7725         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7726         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7727         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7728         quantities.reserve( 6 );     quantities.clear();
7729         for ( int iFace = 0; iFace < 6; iFace++ )
7730         {
7731           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7732           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7733                curNodes[ind[1]] == curNodes[ind[3]] )
7734           {
7735             quantities.clear();
7736             break; // opposite nodes stick
7737           }
7738           nodeSet.clear();
7739           for ( iCur = 0; iCur < 4; iCur++ )
7740           {
7741             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7742               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7743           }
7744           if ( nodeSet.size() < 3 )
7745             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7746           else
7747             quantities.push_back( nodeSet.size() );
7748         }
7749         if ( quantities.size() >= 4 )
7750         {
7751           nbResElems = 1;
7752           nbUniqueNodes = poly_nodes.size();
7753           newElemDefs[0].SetPoly(true);
7754         }
7755       }
7756       break;
7757     } // case HEXAHEDRON
7758
7759     default:
7760       toRemove = true;
7761
7762     } // switch ( entity )
7763
7764     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7765     {
7766       // erase from nodeNodeMap nodes whose merge spoils elem
7767       vector< const SMDS_MeshNode* > noMergeNodes;
7768       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7769       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7770         nodeNodeMap.erase( noMergeNodes[i] );
7771     }
7772     
7773   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7774
7775   uniqueNodes.resize( nbUniqueNodes );
7776
7777   if ( !toRemove && nbResElems == 0 )
7778     nbResElems = 1;
7779
7780   newElemDefs.resize( nbResElems );
7781
7782   return !toRemove;
7783 }
7784
7785
7786 // ========================================================
7787 // class   : ComparableElement
7788 // purpose : allow comparing elements basing on their nodes
7789 // ========================================================
7790
7791 class ComparableElement : public boost::container::flat_set< smIdType >
7792 {
7793   typedef boost::container::flat_set< smIdType >  int_set;
7794
7795   const SMDS_MeshElement* myElem;
7796   smIdType                mySumID;
7797   mutable int             myGroupID;
7798
7799 public:
7800
7801   ComparableElement( const SMDS_MeshElement* theElem ):
7802     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7803   {
7804     this->reserve( theElem->NbNodes() );
7805     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7806     {
7807       smIdType id = nodeIt->next()->GetID();
7808       mySumID += id;
7809       this->insert( id );
7810     }
7811   }
7812
7813   const SMDS_MeshElement* GetElem() const { return myElem; }
7814
7815   int& GroupID() const { return myGroupID; }
7816   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7817
7818   ComparableElement( const ComparableElement& theSource ) // move copy
7819     : int_set()
7820   {
7821     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7822     (int_set&) (*this ) = std::move( src );
7823     myElem    = src.myElem;
7824     mySumID   = src.mySumID;
7825     myGroupID = src.myGroupID;
7826   }
7827
7828   static int HashCode(const ComparableElement& se, int limit )
7829   {
7830     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7831   }
7832   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7833   {
7834     return ( se1 == se2 );
7835   }
7836
7837 };
7838
7839 //=======================================================================
7840 //function : FindEqualElements
7841 //purpose  : Return list of group of elements built on the same nodes.
7842 //           Search among theElements or in the whole mesh if theElements is empty
7843 //=======================================================================
7844
7845 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7846                                           TListOfListOfElementsID & theGroupsOfElementsID )
7847 {
7848   ClearLastCreated();
7849
7850   SMDS_ElemIteratorPtr elemIt;
7851   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7852   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7853
7854   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7855   typedef std::list<smIdType>                                     TGroupOfElems;
7856   TMapOfElements               mapOfElements;
7857   std::vector< TGroupOfElems > arrayOfGroups;
7858   TGroupOfElems                groupOfElems;
7859
7860   while ( elemIt->more() )
7861   {
7862     const SMDS_MeshElement* curElem = elemIt->next();
7863     if ( curElem->IsNull() )
7864       continue;
7865     ComparableElement      compElem = curElem;
7866     // check uniqueness
7867     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7868     if ( elemInSet.GetElem() != curElem ) // coincident elem
7869     {
7870       int& iG = elemInSet.GroupID();
7871       if ( iG < 0 )
7872       {
7873         iG = arrayOfGroups.size();
7874         arrayOfGroups.push_back( groupOfElems );
7875         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7876       }
7877       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7878     }
7879   }
7880
7881   groupOfElems.clear();
7882   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7883   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7884   {
7885     if ( groupIt->size() > 1 ) {
7886       //groupOfElems.sort(); -- theElements are sorted already
7887       theGroupsOfElementsID.emplace_back( *groupIt );
7888     }
7889   }
7890 }
7891
7892 //=======================================================================
7893 //function : MergeElements
7894 //purpose  : In each given group, substitute all elements by the first one.
7895 //=======================================================================
7896
7897 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7898 {
7899   ClearLastCreated();
7900
7901   typedef list<smIdType> TListOfIDs;
7902   TListOfIDs rmElemIds; // IDs of elems to remove
7903
7904   SMESHDS_Mesh* aMesh = GetMeshDS();
7905
7906   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7907   while ( groupsIt != theGroupsOfElementsID.end() ) {
7908     TListOfIDs& aGroupOfElemID = *groupsIt;
7909     aGroupOfElemID.sort();
7910     int elemIDToKeep = aGroupOfElemID.front();
7911     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7912     aGroupOfElemID.pop_front();
7913     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7914     while ( idIt != aGroupOfElemID.end() ) {
7915       int elemIDToRemove = *idIt;
7916       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7917       // add the kept element in groups of removed one (PAL15188)
7918       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7919       rmElemIds.push_back( elemIDToRemove );
7920       ++idIt;
7921     }
7922     ++groupsIt;
7923   }
7924
7925   Remove( rmElemIds, false );
7926 }
7927
7928 //=======================================================================
7929 //function : MergeEqualElements
7930 //purpose  : Remove all but one of elements built on the same nodes.
7931 //=======================================================================
7932
7933 void SMESH_MeshEditor::MergeEqualElements()
7934 {
7935   TIDSortedElemSet aMeshElements; /* empty input ==
7936                                      to merge equal elements in the whole mesh */
7937   TListOfListOfElementsID aGroupsOfElementsID;
7938   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7939   MergeElements( aGroupsOfElementsID );
7940 }
7941
7942 //=======================================================================
7943 //function : findAdjacentFace
7944 //purpose  :
7945 //=======================================================================
7946
7947 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7948                                                 const SMDS_MeshNode* n2,
7949                                                 const SMDS_MeshElement* elem)
7950 {
7951   TIDSortedElemSet elemSet, avoidSet;
7952   if ( elem )
7953     avoidSet.insert ( elem );
7954   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7955 }
7956
7957 //=======================================================================
7958 //function : findSegment
7959 //purpose  : Return a mesh segment by two nodes one of which can be medium
7960 //=======================================================================
7961
7962 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7963                                            const SMDS_MeshNode* n2)
7964 {
7965   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7966   while ( it->more() )
7967   {
7968     const SMDS_MeshElement* seg = it->next();
7969     if ( seg->GetNodeIndex( n2 ) >= 0 )
7970       return seg;
7971   }
7972   return 0;
7973 }
7974
7975 //=======================================================================
7976 //function : FindFreeBorder
7977 //purpose  :
7978 //=======================================================================
7979
7980 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7981
7982 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7983                                        const SMDS_MeshNode*             theSecondNode,
7984                                        const SMDS_MeshNode*             theLastNode,
7985                                        list< const SMDS_MeshNode* > &   theNodes,
7986                                        list< const SMDS_MeshElement* >& theFaces)
7987 {
7988   if ( !theFirstNode || !theSecondNode )
7989     return false;
7990   // find border face between theFirstNode and theSecondNode
7991   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7992   if ( !curElem )
7993     return false;
7994
7995   theFaces.push_back( curElem );
7996   theNodes.push_back( theFirstNode );
7997   theNodes.push_back( theSecondNode );
7998
7999   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8000   //TIDSortedElemSet foundElems;
8001   bool needTheLast = ( theLastNode != 0 );
8002
8003   vector<const SMDS_MeshNode*> nodes;
8004   
8005   while ( nStart != theLastNode ) {
8006     if ( nStart == theFirstNode )
8007       return !needTheLast;
8008
8009     // find all free border faces sharing nStart
8010
8011     list< const SMDS_MeshElement* > curElemList;
8012     list< const SMDS_MeshNode* >    nStartList;
8013     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8014     while ( invElemIt->more() ) {
8015       const SMDS_MeshElement* e = invElemIt->next();
8016       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8017       {
8018         // get nodes
8019         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8020                       SMDS_MeshElement::iterator() );
8021         nodes.push_back( nodes[ 0 ]);
8022
8023         // check 2 links
8024         int iNode = 0, nbNodes = nodes.size() - 1;
8025         for ( iNode = 0; iNode < nbNodes; iNode++ )
8026           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8027                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8028               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8029           {
8030             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8031             curElemList.push_back( e );
8032           }
8033       }
8034     }
8035     // analyse the found
8036
8037     int nbNewBorders = curElemList.size();
8038     if ( nbNewBorders == 0 ) {
8039       // no free border furthermore
8040       return !needTheLast;
8041     }
8042     else if ( nbNewBorders == 1 ) {
8043       // one more element found
8044       nIgnore = nStart;
8045       nStart = nStartList.front();
8046       curElem = curElemList.front();
8047       theFaces.push_back( curElem );
8048       theNodes.push_back( nStart );
8049     }
8050     else {
8051       // several continuations found
8052       list< const SMDS_MeshElement* >::iterator curElemIt;
8053       list< const SMDS_MeshNode* >::iterator nStartIt;
8054       // check if one of them reached the last node
8055       if ( needTheLast ) {
8056         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8057              curElemIt!= curElemList.end();
8058              curElemIt++, nStartIt++ )
8059           if ( *nStartIt == theLastNode ) {
8060             theFaces.push_back( *curElemIt );
8061             theNodes.push_back( *nStartIt );
8062             return true;
8063           }
8064       }
8065       // find the best free border by the continuations
8066       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8067       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8068       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8069            curElemIt!= curElemList.end();
8070            curElemIt++, nStartIt++ )
8071       {
8072         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8073         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8074         // find one more free border
8075         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8076           cNL->clear();
8077           cFL->clear();
8078         }
8079         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8080           // choice: clear a worse one
8081           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8082           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8083           contNodes[ iWorse ].clear();
8084           contFaces[ iWorse ].clear();
8085         }
8086       }
8087       if ( contNodes[0].empty() && contNodes[1].empty() )
8088         return false;
8089
8090       // push_back the best free border
8091       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8092       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8093       //theNodes.pop_back(); // remove nIgnore
8094       theNodes.pop_back(); // remove nStart
8095       //theFaces.pop_back(); // remove curElem
8096       theNodes.splice( theNodes.end(), *cNL );
8097       theFaces.splice( theFaces.end(), *cFL );
8098       return true;
8099
8100     } // several continuations found
8101   } // while ( nStart != theLastNode )
8102
8103   return true;
8104 }
8105
8106 //=======================================================================
8107 //function : CheckFreeBorderNodes
8108 //purpose  : Return true if the tree nodes are on a free border
8109 //=======================================================================
8110
8111 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8112                                             const SMDS_MeshNode* theNode2,
8113                                             const SMDS_MeshNode* theNode3)
8114 {
8115   list< const SMDS_MeshNode* > nodes;
8116   list< const SMDS_MeshElement* > faces;
8117   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8118 }
8119
8120 //=======================================================================
8121 //function : SewFreeBorder
8122 //purpose  :
8123 //warning  : for border-to-side sewing theSideSecondNode is considered as
8124 //           the last side node and theSideThirdNode is not used
8125 //=======================================================================
8126
8127 SMESH_MeshEditor::Sew_Error
8128 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8129                                  const SMDS_MeshNode* theBordSecondNode,
8130                                  const SMDS_MeshNode* theBordLastNode,
8131                                  const SMDS_MeshNode* theSideFirstNode,
8132                                  const SMDS_MeshNode* theSideSecondNode,
8133                                  const SMDS_MeshNode* theSideThirdNode,
8134                                  const bool           theSideIsFreeBorder,
8135                                  const bool           toCreatePolygons,
8136                                  const bool           toCreatePolyedrs)
8137 {
8138   ClearLastCreated();
8139
8140   Sew_Error aResult = SEW_OK;
8141
8142   // ====================================
8143   //    find side nodes and elements
8144   // ====================================
8145
8146   list< const SMDS_MeshNode* >    nSide[ 2 ];
8147   list< const SMDS_MeshElement* > eSide[ 2 ];
8148   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8149   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8150
8151   // Free border 1
8152   // --------------
8153   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8154                       nSide[0], eSide[0])) {
8155     MESSAGE(" Free Border 1 not found " );
8156     aResult = SEW_BORDER1_NOT_FOUND;
8157   }
8158   if (theSideIsFreeBorder) {
8159     // Free border 2
8160     // --------------
8161     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8162                         nSide[1], eSide[1])) {
8163       MESSAGE(" Free Border 2 not found " );
8164       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8165     }
8166   }
8167   if ( aResult != SEW_OK )
8168     return aResult;
8169
8170   if (!theSideIsFreeBorder) {
8171     // Side 2
8172     // --------------
8173
8174     // -------------------------------------------------------------------------
8175     // Algo:
8176     // 1. If nodes to merge are not coincident, move nodes of the free border
8177     //    from the coord sys defined by the direction from the first to last
8178     //    nodes of the border to the correspondent sys of the side 2
8179     // 2. On the side 2, find the links most co-directed with the correspondent
8180     //    links of the free border
8181     // -------------------------------------------------------------------------
8182
8183     // 1. Since sewing may break if there are volumes to split on the side 2,
8184     //    we won't move nodes but just compute new coordinates for them
8185     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8186     TNodeXYZMap nBordXYZ;
8187     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8188     list< const SMDS_MeshNode* >::iterator nBordIt;
8189
8190     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8191     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8192     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8193     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8194     double tol2 = 1.e-8;
8195     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8196     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8197       // Need node movement.
8198
8199       // find X and Z axes to create trsf
8200       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8201       gp_Vec X = Zs ^ Zb;
8202       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8203         // Zb || Zs
8204         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8205
8206       // coord systems
8207       gp_Ax3 toBordAx( Pb1, Zb, X );
8208       gp_Ax3 fromSideAx( Ps1, Zs, X );
8209       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8210       // set trsf
8211       gp_Trsf toBordSys, fromSide2Sys;
8212       toBordSys.SetTransformation( toBordAx );
8213       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8214       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8215
8216       // move
8217       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8218         const SMDS_MeshNode* n = *nBordIt;
8219         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8220         toBordSys.Transforms( xyz );
8221         fromSide2Sys.Transforms( xyz );
8222         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8223       }
8224     }
8225     else {
8226       // just insert nodes XYZ in the nBordXYZ map
8227       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8228         const SMDS_MeshNode* n = *nBordIt;
8229         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8230       }
8231     }
8232
8233     // 2. On the side 2, find the links most co-directed with the correspondent
8234     //    links of the free border
8235
8236     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8237     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8238     sideNodes.push_back( theSideFirstNode );
8239
8240     bool hasVolumes = false;
8241     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8242     set<long> foundSideLinkIDs, checkedLinkIDs;
8243     SMDS_VolumeTool volume;
8244     //const SMDS_MeshNode* faceNodes[ 4 ];
8245
8246     const SMDS_MeshNode*    sideNode;
8247     const SMDS_MeshElement* sideElem  = 0;
8248     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8249     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8250     nBordIt = bordNodes.begin();
8251     nBordIt++;
8252     // border node position and border link direction to compare with
8253     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8254     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8255     // choose next side node by link direction or by closeness to
8256     // the current border node:
8257     bool searchByDir = ( *nBordIt != theBordLastNode );
8258     do {
8259       // find the next node on the Side 2
8260       sideNode = 0;
8261       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8262       long linkID;
8263       checkedLinkIDs.clear();
8264       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8265
8266       // loop on inverse elements of current node (prevSideNode) on the Side 2
8267       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8268       while ( invElemIt->more() )
8269       {
8270         const SMDS_MeshElement* elem = invElemIt->next();
8271         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8272         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8273         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8274         bool isVolume = volume.Set( elem );
8275         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8276         if ( isVolume ) // --volume
8277           hasVolumes = true;
8278         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8279           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8280           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8281           while ( nIt->more() ) {
8282             nodes[ iNode ] = cast2Node( nIt->next() );
8283             if ( nodes[ iNode++ ] == prevSideNode )
8284               iPrevNode = iNode - 1;
8285           }
8286           // there are 2 links to check
8287           nbNodes = 2;
8288         }
8289         else // --edge
8290           continue;
8291         // loop on links, to be precise, on the second node of links
8292         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8293           const SMDS_MeshNode* n = nodes[ iNode ];
8294           if ( isVolume ) {
8295             if ( !volume.IsLinked( n, prevSideNode ))
8296               continue;
8297           }
8298           else {
8299             if ( iNode ) // a node before prevSideNode
8300               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8301             else         // a node after prevSideNode
8302               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8303           }
8304           // check if this link was already used
8305           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8306           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8307           if (!isJustChecked &&
8308               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8309           {
8310             // test a link geometrically
8311             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8312             bool linkIsBetter = false;
8313             double dot = 0.0, dist = 0.0;
8314             if ( searchByDir ) { // choose most co-directed link
8315               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8316               linkIsBetter = ( dot > maxDot );
8317             }
8318             else { // choose link with the node closest to bordPos
8319               dist = ( nextXYZ - bordPos ).SquareModulus();
8320               linkIsBetter = ( dist < minDist );
8321             }
8322             if ( linkIsBetter ) {
8323               maxDot = dot;
8324               minDist = dist;
8325               linkID = iLink;
8326               sideNode = n;
8327               sideElem = elem;
8328             }
8329           }
8330         }
8331       } // loop on inverse elements of prevSideNode
8332
8333       if ( !sideNode ) {
8334         MESSAGE(" Can't find path by links of the Side 2 ");
8335         return SEW_BAD_SIDE_NODES;
8336       }
8337       sideNodes.push_back( sideNode );
8338       sideElems.push_back( sideElem );
8339       foundSideLinkIDs.insert ( linkID );
8340       prevSideNode = sideNode;
8341
8342       if ( *nBordIt == theBordLastNode )
8343         searchByDir = false;
8344       else {
8345         // find the next border link to compare with
8346         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8347         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8348         // move to next border node if sideNode is before forward border node (bordPos)
8349         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8350           prevBordNode = *nBordIt;
8351           nBordIt++;
8352           bordPos = nBordXYZ[ *nBordIt ];
8353           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8354           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8355         }
8356       }
8357     }
8358     while ( sideNode != theSideSecondNode );
8359
8360     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8361       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8362       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8363     }
8364   } // end nodes search on the side 2
8365
8366   // ============================
8367   // sew the border to the side 2
8368   // ============================
8369
8370   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8371   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8372
8373   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8374   if ( toMergeConformal && toCreatePolygons )
8375   {
8376     // do not merge quadrangles if polygons are OK (IPAL0052824)
8377     eIt[0] = eSide[0].begin();
8378     eIt[1] = eSide[1].begin();
8379     bool allQuads[2] = { true, true };
8380     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8382         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8383     }
8384     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8385   }
8386
8387   TListOfListOfNodes nodeGroupsToMerge;
8388   if (( toMergeConformal ) ||
8389       ( theSideIsFreeBorder && !theSideThirdNode )) {
8390
8391     // all nodes are to be merged
8392
8393     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8394          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8395          nIt[0]++, nIt[1]++ )
8396     {
8397       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8398       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8399       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8400     }
8401   }
8402   else {
8403
8404     // insert new nodes into the border and the side to get equal nb of segments
8405
8406     // get normalized parameters of nodes on the borders
8407     vector< double > param[ 2 ];
8408     param[0].resize( maxNbNodes );
8409     param[1].resize( maxNbNodes );
8410     int iNode, iBord;
8411     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8412       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8413       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8414       const SMDS_MeshNode* nPrev = *nIt;
8415       double bordLength = 0;
8416       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8417         const SMDS_MeshNode* nCur = *nIt;
8418         gp_XYZ segment (nCur->X() - nPrev->X(),
8419                         nCur->Y() - nPrev->Y(),
8420                         nCur->Z() - nPrev->Z());
8421         double segmentLen = segment.Modulus();
8422         bordLength += segmentLen;
8423         param[ iBord ][ iNode ] = bordLength;
8424         nPrev = nCur;
8425       }
8426       // normalize within [0,1]
8427       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8428         param[ iBord ][ iNode ] /= bordLength;
8429       }
8430     }
8431
8432     // loop on border segments
8433     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8434     int i[ 2 ] = { 0, 0 };
8435     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8436     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8437
8438     // element can be split while iterating on border if it has two edges in the border
8439     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8440     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8441
8442     TElemOfNodeListMap insertMap;
8443     TElemOfNodeListMap::iterator insertMapIt;
8444     // insertMap is
8445     // key:   elem to insert nodes into
8446     // value: 2 nodes to insert between + nodes to be inserted
8447     do {
8448       bool next[ 2 ] = { false, false };
8449
8450       // find min adjacent segment length after sewing
8451       double nextParam = 10., prevParam = 0;
8452       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8453         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8454           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8455         if ( i[ iBord ] > 0 )
8456           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8457       }
8458       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8459       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8460       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8461
8462       // choose to insert or to merge nodes
8463       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8464       if ( Abs( du ) <= minSegLen * 0.2 ) {
8465         // merge
8466         // ------
8467         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8468         const SMDS_MeshNode* n0 = *nIt[0];
8469         const SMDS_MeshNode* n1 = *nIt[1];
8470         nodeGroupsToMerge.back().push_back( n1 );
8471         nodeGroupsToMerge.back().push_back( n0 );
8472         // position of node of the border changes due to merge
8473         param[ 0 ][ i[0] ] += du;
8474         // move n1 for the sake of elem shape evaluation during insertion.
8475         // n1 will be removed by MergeNodes() anyway
8476         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8477         next[0] = next[1] = true;
8478       }
8479       else {
8480         // insert
8481         // ------
8482         int intoBord = ( du < 0 ) ? 0 : 1;
8483         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8484         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8485         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8486         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8487         if ( intoBord == 1 ) {
8488           // move node of the border to be on a link of elem of the side
8489           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8490           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8491           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8492           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8493         }
8494         elemReplaceMapIt = elemReplaceMap.find( elem );
8495         if ( elemReplaceMapIt != elemReplaceMap.end() )
8496           elem = elemReplaceMapIt->second;
8497
8498         insertMapIt = insertMap.find( elem );
8499         bool  notFound = ( insertMapIt == insertMap.end() );
8500         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8501         if ( otherLink ) {
8502           // insert into another link of the same element:
8503           // 1. perform insertion into the other link of the elem
8504           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8505           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8506           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8507           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8508           // 2. perform insertion into the link of adjacent faces
8509           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8510             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8511           }
8512           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8513             InsertNodesIntoLink( seg, n12, n22, nodeList );
8514           }
8515           if (toCreatePolyedrs) {
8516             // perform insertion into the links of adjacent volumes
8517             UpdateVolumes(n12, n22, nodeList);
8518           }
8519           // 3. find an element appeared on n1 and n2 after the insertion
8520           insertMap.erase( insertMapIt );
8521           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8522           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8523           elem = elem2;
8524         }
8525         if ( notFound || otherLink ) {
8526           // add element and nodes of the side into the insertMap
8527           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8528           (*insertMapIt).second.push_back( n1 );
8529           (*insertMapIt).second.push_back( n2 );
8530         }
8531         // add node to be inserted into elem
8532         (*insertMapIt).second.push_back( nIns );
8533         next[ 1 - intoBord ] = true;
8534       }
8535
8536       // go to the next segment
8537       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8538         if ( next[ iBord ] ) {
8539           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8540             eIt[ iBord ]++;
8541           nPrev[ iBord ] = *nIt[ iBord ];
8542           nIt[ iBord ]++; i[ iBord ]++;
8543         }
8544       }
8545     }
8546     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8547
8548     // perform insertion of nodes into elements
8549
8550     for (insertMapIt = insertMap.begin();
8551          insertMapIt != insertMap.end();
8552          insertMapIt++ )
8553     {
8554       const SMDS_MeshElement* elem = (*insertMapIt).first;
8555       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8556       if ( nodeList.size() < 3 ) continue;
8557       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8558       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8559
8560       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8561
8562       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8563         InsertNodesIntoLink( seg, n1, n2, nodeList );
8564       }
8565
8566       if ( !theSideIsFreeBorder ) {
8567         // look for and insert nodes into the faces adjacent to elem
8568         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8569           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8570         }
8571       }
8572       if (toCreatePolyedrs) {
8573         // perform insertion into the links of adjacent volumes
8574         UpdateVolumes(n1, n2, nodeList);
8575       }
8576     }
8577   } // end: insert new nodes
8578
8579   MergeNodes ( nodeGroupsToMerge );
8580
8581
8582   // Remove coincident segments
8583
8584   // get new segments
8585   TIDSortedElemSet segments;
8586   SMESH_SequenceOfElemPtr newFaces;
8587   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8588   {
8589     if ( !myLastCreatedElems[i] ) continue;
8590     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8591       segments.insert( segments.end(), myLastCreatedElems[i] );
8592     else
8593       newFaces.push_back( myLastCreatedElems[i] );
8594   }
8595   // get segments adjacent to merged nodes
8596   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8597   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8598   {
8599     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8600     if ( nodes.front()->IsNull() ) continue;
8601     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8602     while ( segIt->more() )
8603       segments.insert( segIt->next() );
8604   }
8605
8606   // find coincident
8607   TListOfListOfElementsID equalGroups;
8608   if ( !segments.empty() )
8609     FindEqualElements( segments, equalGroups );
8610   if ( !equalGroups.empty() )
8611   {
8612     // remove from segments those that will be removed
8613     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8614     for ( ; itGroups != equalGroups.end(); ++itGroups )
8615     {
8616       list< smIdType >& group = *itGroups;
8617       list< smIdType >::iterator id = group.begin();
8618       for ( ++id; id != group.end(); ++id )
8619         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8620           segments.erase( seg );
8621     }
8622     // remove equal segments
8623     MergeElements( equalGroups );
8624
8625     // restore myLastCreatedElems
8626     myLastCreatedElems = newFaces;
8627     TIDSortedElemSet::iterator seg = segments.begin();
8628     for ( ; seg != segments.end(); ++seg )
8629       myLastCreatedElems.push_back( *seg );
8630   }
8631
8632   return aResult;
8633 }
8634
8635 //=======================================================================
8636 //function : InsertNodesIntoLink
8637 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8638 //           and theBetweenNode2 and split theElement
8639 //=======================================================================
8640
8641 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8642                                            const SMDS_MeshNode*        theBetweenNode1,
8643                                            const SMDS_MeshNode*        theBetweenNode2,
8644                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8645                                            const bool                  toCreatePoly)
8646 {
8647   if ( !theElement ) return;
8648
8649   SMESHDS_Mesh *aMesh = GetMeshDS();
8650   vector<const SMDS_MeshElement*> newElems;
8651
8652   if ( theElement->GetType() == SMDSAbs_Edge )
8653   {
8654     theNodesToInsert.push_front( theBetweenNode1 );
8655     theNodesToInsert.push_back ( theBetweenNode2 );
8656     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8657     const SMDS_MeshNode* n1 = *n;
8658     for ( ++n; n != theNodesToInsert.end(); ++n )
8659     {
8660       const SMDS_MeshNode* n2 = *n;
8661       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8662         AddToSameGroups( seg, theElement, aMesh );
8663       else
8664         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8665       n1 = n2;
8666     }
8667     theNodesToInsert.pop_front();
8668     theNodesToInsert.pop_back();
8669
8670     if ( theElement->IsQuadratic() ) // add a not split part
8671     {
8672       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8673                                           theElement->end_nodes() );
8674       int iOther = 0, nbN = nodes.size();
8675       for ( ; iOther < nbN; ++iOther )
8676         if ( nodes[iOther] != theBetweenNode1 &&
8677              nodes[iOther] != theBetweenNode2 )
8678           break;
8679       if      ( iOther == 0 )
8680       {
8681         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8682           AddToSameGroups( seg, theElement, aMesh );
8683         else
8684           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8685       }
8686       else if ( iOther == 2 )
8687       {
8688         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8689           AddToSameGroups( seg, theElement, aMesh );
8690         else
8691           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8692       }
8693     }
8694     // treat new elements
8695     for ( size_t i = 0; i < newElems.size(); ++i )
8696       if ( newElems[i] )
8697       {
8698         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8699         myLastCreatedElems.push_back( newElems[i] );
8700       }
8701     ReplaceElemInGroups( theElement, newElems, aMesh );
8702     aMesh->RemoveElement( theElement );
8703     return;
8704
8705   } // if ( theElement->GetType() == SMDSAbs_Edge )
8706
8707   const SMDS_MeshElement* theFace = theElement;
8708   if ( theFace->GetType() != SMDSAbs_Face ) return;
8709
8710   // find indices of 2 link nodes and of the rest nodes
8711   int iNode = 0, il1, il2, i3, i4;
8712   il1 = il2 = i3 = i4 = -1;
8713   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8714
8715   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8716   while ( nodeIt->more() ) {
8717     const SMDS_MeshNode* n = nodeIt->next();
8718     if ( n == theBetweenNode1 )
8719       il1 = iNode;
8720     else if ( n == theBetweenNode2 )
8721       il2 = iNode;
8722     else if ( i3 < 0 )
8723       i3 = iNode;
8724     else
8725       i4 = iNode;
8726     nodes[ iNode++ ] = n;
8727   }
8728   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8729     return ;
8730
8731   // arrange link nodes to go one after another regarding the face orientation
8732   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8733   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8734   if ( reverse ) {
8735     iNode = il1;
8736     il1 = il2;
8737     il2 = iNode;
8738     aNodesToInsert.reverse();
8739   }
8740   // check that not link nodes of a quadrangles are in good order
8741   int nbFaceNodes = theFace->NbNodes();
8742   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8743     iNode = i3;
8744     i3 = i4;
8745     i4 = iNode;
8746   }
8747
8748   if (toCreatePoly || theFace->IsPoly()) {
8749
8750     iNode = 0;
8751     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8752
8753     // add nodes of face up to first node of link
8754     bool isFLN = false;
8755     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8756     while ( nodeIt->more() && !isFLN ) {
8757       const SMDS_MeshNode* n = nodeIt->next();
8758       poly_nodes[iNode++] = n;
8759       isFLN = ( n == nodes[il1] );
8760     }
8761     // add nodes to insert
8762     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8763     for (; nIt != aNodesToInsert.end(); nIt++) {
8764       poly_nodes[iNode++] = *nIt;
8765     }
8766     // add nodes of face starting from last node of link
8767     while ( nodeIt->more() ) {
8768       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769       poly_nodes[iNode++] = n;
8770     }
8771
8772     // make a new face
8773     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8774   }
8775
8776   else if ( !theFace->IsQuadratic() )
8777   {
8778     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8779     int nbLinkNodes = 2 + aNodesToInsert.size();
8780     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8781     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8782     linkNodes[ 0 ] = nodes[ il1 ];
8783     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8784     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8785     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8786       linkNodes[ iNode++ ] = *nIt;
8787     }
8788     // decide how to split a quadrangle: compare possible variants
8789     // and choose which of splits to be a quadrangle
8790     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8791     if ( nbFaceNodes == 3 ) {
8792       iBestQuad = nbSplits;
8793       i4 = i3;
8794     }
8795     else if ( nbFaceNodes == 4 ) {
8796       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8797       double aBestRate = DBL_MAX;
8798       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8799         i1 = 0; i2 = 1;
8800         double aBadRate = 0;
8801         // evaluate elements quality
8802         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8803           if ( iSplit == iQuad ) {
8804             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8805                                    linkNodes[ i2++ ],
8806                                    nodes[ i3 ],
8807                                    nodes[ i4 ]);
8808             aBadRate += getBadRate( &quad, aCrit );
8809           }
8810           else {
8811             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8812                                    linkNodes[ i2++ ],
8813                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8814             aBadRate += getBadRate( &tria, aCrit );
8815           }
8816         }
8817         // choice
8818         if ( aBadRate < aBestRate ) {
8819           iBestQuad = iQuad;
8820           aBestRate = aBadRate;
8821         }
8822       }
8823     }
8824
8825     // create new elements
8826     i1 = 0; i2 = 1;
8827     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8828     {
8829       if ( iSplit == iBestQuad )
8830         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8831                                             linkNodes[ i2++ ],
8832                                             nodes[ i3 ],
8833                                             nodes[ i4 ]));
8834       else
8835         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8836                                             linkNodes[ i2++ ],
8837                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8838     }
8839
8840     const SMDS_MeshNode* newNodes[ 4 ];
8841     newNodes[ 0 ] = linkNodes[ i1 ];
8842     newNodes[ 1 ] = linkNodes[ i2 ];
8843     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8844     newNodes[ 3 ] = nodes[ i4 ];
8845     if (iSplit == iBestQuad)
8846       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8847     else
8848       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8849
8850   } // end if(!theFace->IsQuadratic())
8851
8852   else { // theFace is quadratic
8853     // we have to split theFace on simple triangles and one simple quadrangle
8854     int tmp = il1/2;
8855     int nbshift = tmp*2;
8856     // shift nodes in nodes[] by nbshift
8857     int i,j;
8858     for(i=0; i<nbshift; i++) {
8859       const SMDS_MeshNode* n = nodes[0];
8860       for(j=0; j<nbFaceNodes-1; j++) {
8861         nodes[j] = nodes[j+1];
8862       }
8863       nodes[nbFaceNodes-1] = n;
8864     }
8865     il1 = il1 - nbshift;
8866     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8867     //   n0      n1     n2    n0      n1     n2
8868     //     +-----+-----+        +-----+-----+
8869     //      \         /         |           |
8870     //       \       /          |           |
8871     //      n5+     +n3       n7+           +n3
8872     //         \   /            |           |
8873     //          \ /             |           |
8874     //           +              +-----+-----+
8875     //           n4           n6      n5     n4
8876
8877     // create new elements
8878     int n1,n2,n3;
8879     if ( nbFaceNodes == 6 ) { // quadratic triangle
8880       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8881       if ( theFace->IsMediumNode(nodes[il1]) ) {
8882         // create quadrangle
8883         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8884         n1 = 1;
8885         n2 = 2;
8886         n3 = 3;
8887       }
8888       else {
8889         // create quadrangle
8890         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8891         n1 = 0;
8892         n2 = 1;
8893         n3 = 5;
8894       }
8895     }
8896     else { // nbFaceNodes==8 - quadratic quadrangle
8897       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8898       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8899       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8900       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8901         // create quadrangle
8902         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8903         n1 = 1;
8904         n2 = 2;
8905         n3 = 3;
8906       }
8907       else {
8908         // create quadrangle
8909         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8910         n1 = 0;
8911         n2 = 1;
8912         n3 = 7;
8913       }
8914     }
8915     // create needed triangles using n1,n2,n3 and inserted nodes
8916     int nbn = 2 + aNodesToInsert.size();
8917     vector<const SMDS_MeshNode*> aNodes(nbn);
8918     aNodes[0    ] = nodes[n1];
8919     aNodes[nbn-1] = nodes[n2];
8920     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8921     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8922       aNodes[iNode++] = *nIt;
8923     }
8924     for ( i = 1; i < nbn; i++ )
8925       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8926   }
8927
8928   // remove the old face
8929   for ( size_t i = 0; i < newElems.size(); ++i )
8930     if ( newElems[i] )
8931     {
8932       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8933       myLastCreatedElems.push_back( newElems[i] );
8934     }
8935   ReplaceElemInGroups( theFace, newElems, aMesh );
8936   aMesh->RemoveElement(theFace);
8937
8938 } // InsertNodesIntoLink()
8939
8940 //=======================================================================
8941 //function : UpdateVolumes
8942 //purpose  :
8943 //=======================================================================
8944
8945 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8946                                       const SMDS_MeshNode*        theBetweenNode2,
8947                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8948 {
8949   ClearLastCreated();
8950
8951   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8952   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8953     const SMDS_MeshElement* elem = invElemIt->next();
8954
8955     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8956     SMDS_VolumeTool aVolume (elem);
8957     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8958       continue;
8959
8960     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8961     int iface, nbFaces = aVolume.NbFaces();
8962     vector<const SMDS_MeshNode *> poly_nodes;
8963     vector<int> quantities (nbFaces);
8964
8965     for (iface = 0; iface < nbFaces; iface++) {
8966       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8967       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8968       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8969
8970       for (int inode = 0; inode < nbFaceNodes; inode++) {
8971         poly_nodes.push_back(faceNodes[inode]);
8972
8973         if (nbInserted == 0) {
8974           if (faceNodes[inode] == theBetweenNode1) {
8975             if (faceNodes[inode + 1] == theBetweenNode2) {
8976               nbInserted = theNodesToInsert.size();
8977
8978               // add nodes to insert
8979               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8980               for (; nIt != theNodesToInsert.end(); nIt++) {
8981                 poly_nodes.push_back(*nIt);
8982               }
8983             }
8984           }
8985           else if (faceNodes[inode] == theBetweenNode2) {
8986             if (faceNodes[inode + 1] == theBetweenNode1) {
8987               nbInserted = theNodesToInsert.size();
8988
8989               // add nodes to insert in reversed order
8990               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8991               nIt--;
8992               for (; nIt != theNodesToInsert.begin(); nIt--) {
8993                 poly_nodes.push_back(*nIt);
8994               }
8995               poly_nodes.push_back(*nIt);
8996             }
8997           }
8998           else {
8999           }
9000         }
9001       }
9002       quantities[iface] = nbFaceNodes + nbInserted;
9003     }
9004
9005     // Replace the volume
9006     SMESHDS_Mesh *aMesh = GetMeshDS();
9007
9008     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9009     {
9010       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9011       myLastCreatedElems.push_back( newElem );
9012       ReplaceElemInGroups( elem, newElem, aMesh );
9013     }
9014     aMesh->RemoveElement( elem );
9015   }
9016 }
9017
9018 namespace
9019 {
9020   //================================================================================
9021   /*!
9022    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9023    */
9024   //================================================================================
9025
9026   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9027                            vector<const SMDS_MeshNode *> & nodes,
9028                            vector<int> &                   nbNodeInFaces )
9029   {
9030     nodes.clear();
9031     nbNodeInFaces.clear();
9032     SMDS_VolumeTool vTool ( elem );
9033     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9034     {
9035       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9036       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9037       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9038     }
9039   }
9040 }
9041
9042 //=======================================================================
9043 /*!
9044  * \brief Convert elements contained in a sub-mesh to quadratic
9045  * \return int - nb of checked elements
9046  */
9047 //=======================================================================
9048
9049 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9050                                                   SMESH_MesherHelper& theHelper,
9051                                                   const bool          theForce3d)
9052 {
9053   //MESSAGE("convertElemToQuadratic");
9054   smIdType nbElem = 0;
9055   if( !theSm ) return nbElem;
9056
9057   vector<int> nbNodeInFaces;
9058   vector<const SMDS_MeshNode *> nodes;
9059   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9060   while(ElemItr->more())
9061   {
9062     nbElem++;
9063     const SMDS_MeshElement* elem = ElemItr->next();
9064     if( !elem ) continue;
9065
9066     // analyse a necessity of conversion
9067     const SMDSAbs_ElementType aType = elem->GetType();
9068     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9069       continue;
9070     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9071     bool hasCentralNodes = false;
9072     if ( elem->IsQuadratic() )
9073     {
9074       bool alreadyOK;
9075       switch ( aGeomType ) {
9076       case SMDSEntity_Quad_Triangle:
9077       case SMDSEntity_Quad_Quadrangle:
9078       case SMDSEntity_Quad_Hexa:
9079       case SMDSEntity_Quad_Penta:
9080         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9081
9082       case SMDSEntity_BiQuad_Triangle:
9083       case SMDSEntity_BiQuad_Quadrangle:
9084       case SMDSEntity_TriQuad_Hexa:
9085       case SMDSEntity_BiQuad_Penta:
9086         alreadyOK = theHelper.GetIsBiQuadratic();
9087         hasCentralNodes = true;
9088         break;
9089       default:
9090         alreadyOK = true;
9091       }
9092       // take into account already present medium nodes
9093       switch ( aType ) {
9094       case SMDSAbs_Volume:
9095         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9096       case SMDSAbs_Face:
9097         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9098       case SMDSAbs_Edge:
9099         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9100       default:;
9101       }
9102       if ( alreadyOK )
9103         continue;
9104     }
9105     // get elem data needed to re-create it
9106     //
9107     const smIdType id = elem->GetID();
9108     const int nbNodes = elem->NbCornerNodes();
9109     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9110     if ( aGeomType == SMDSEntity_Polyhedra )
9111       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9112     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9113       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9114
9115     // remove a linear element
9116     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9117
9118     // remove central nodes of biquadratic elements (biquad->quad conversion)
9119     if ( hasCentralNodes )
9120       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9121         if ( nodes[i]->NbInverseElements() == 0 )
9122           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9123
9124     const SMDS_MeshElement* NewElem = 0;
9125
9126     switch( aType )
9127     {
9128     case SMDSAbs_Edge :
9129     {
9130       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9131       break;
9132     }
9133     case SMDSAbs_Face :
9134     {
9135       switch(nbNodes)
9136       {
9137       case 3:
9138         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9139         break;
9140       case 4:
9141         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9142         break;
9143       default:
9144         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9145       }
9146       break;
9147     }
9148     case SMDSAbs_Volume :
9149     {
9150       switch( aGeomType )
9151       {
9152       case SMDSEntity_Tetra:
9153         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9154         break;
9155       case SMDSEntity_Pyramid:
9156         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9157         break;
9158       case SMDSEntity_Penta:
9159       case SMDSEntity_Quad_Penta:
9160       case SMDSEntity_BiQuad_Penta:
9161         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9162         break;
9163       case SMDSEntity_Hexa:
9164       case SMDSEntity_Quad_Hexa:
9165       case SMDSEntity_TriQuad_Hexa:
9166         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9167                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9168         break;
9169       case SMDSEntity_Hexagonal_Prism:
9170       default:
9171         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9172       }
9173       break;
9174     }
9175     default :
9176       continue;
9177     }
9178     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9179     if( NewElem && NewElem->getshapeId() < 1 )
9180       theSm->AddElement( NewElem );
9181   }
9182   return nbElem;
9183 }
9184 //=======================================================================
9185 //function : ConvertToQuadratic
9186 //purpose  :
9187 //=======================================================================
9188
9189 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9190 {
9191   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9192   SMESHDS_Mesh* meshDS = GetMeshDS();
9193
9194   SMESH_MesherHelper aHelper(*myMesh);
9195
9196   aHelper.SetIsQuadratic( true );
9197   aHelper.SetIsBiQuadratic( theToBiQuad );
9198   aHelper.SetElementsOnShape(true);
9199   aHelper.ToFixNodeParameters( true );
9200
9201   // convert elements assigned to sub-meshes
9202   smIdType nbCheckedElems = 0;
9203   if ( myMesh->HasShapeToMesh() )
9204   {
9205     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9206     {
9207       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9208       while ( smIt->more() ) {
9209         SMESH_subMesh* sm = smIt->next();
9210         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9211           aHelper.SetSubShape( sm->GetSubShape() );
9212           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9213         }
9214       }
9215     }
9216   }
9217
9218   // convert elements NOT assigned to sub-meshes
9219   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9220   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9221   {
9222     aHelper.SetElementsOnShape(false);
9223     SMESHDS_SubMesh *smDS = 0;
9224
9225     // convert edges
9226     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9227     while( aEdgeItr->more() )
9228     {
9229       const SMDS_MeshEdge* edge = aEdgeItr->next();
9230       if ( !edge->IsQuadratic() )
9231       {
9232         smIdType                  id = edge->GetID();
9233         const SMDS_MeshNode* n1 = edge->GetNode(0);
9234         const SMDS_MeshNode* n2 = edge->GetNode(1);
9235
9236         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9237
9238         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9239         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9240       }
9241       else
9242       {
9243         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9244       }
9245     }
9246
9247     // convert faces
9248     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9249     while( aFaceItr->more() )
9250     {
9251       const SMDS_MeshFace* face = aFaceItr->next();
9252       if ( !face ) continue;
9253       
9254       const SMDSAbs_EntityType type = face->GetEntityType();
9255       bool alreadyOK;
9256       switch( type )
9257       {
9258       case SMDSEntity_Quad_Triangle:
9259       case SMDSEntity_Quad_Quadrangle:
9260         alreadyOK = !theToBiQuad;
9261         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9262         break;
9263       case SMDSEntity_BiQuad_Triangle:
9264       case SMDSEntity_BiQuad_Quadrangle:
9265         alreadyOK = theToBiQuad;
9266         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9267         break;
9268       default: alreadyOK = false;
9269       }
9270       if ( alreadyOK )
9271         continue;
9272
9273       const smIdType id = face->GetID();
9274       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9275
9276       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9277
9278       SMDS_MeshFace * NewFace = 0;
9279       switch( type )
9280       {
9281       case SMDSEntity_Triangle:
9282       case SMDSEntity_Quad_Triangle:
9283       case SMDSEntity_BiQuad_Triangle:
9284         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9285         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9286           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9287         break;
9288
9289       case SMDSEntity_Quadrangle:
9290       case SMDSEntity_Quad_Quadrangle:
9291       case SMDSEntity_BiQuad_Quadrangle:
9292         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9293         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9294           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9295         break;
9296
9297       default:;
9298         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9299       }
9300       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9301     }
9302
9303     // convert volumes
9304     vector<int> nbNodeInFaces;
9305     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9306     while(aVolumeItr->more())
9307     {
9308       const SMDS_MeshVolume* volume = aVolumeItr->next();
9309       if ( !volume ) continue;
9310
9311       const SMDSAbs_EntityType type = volume->GetEntityType();
9312       if ( volume->IsQuadratic() )
9313       {
9314         bool alreadyOK;
9315         switch ( type )
9316         {
9317         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9318         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9319         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9320         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9321         default:                      alreadyOK = true;
9322         }
9323         if ( alreadyOK )
9324         {
9325           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9326           continue;
9327         }
9328       }
9329       const smIdType id = volume->GetID();
9330       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9331       if ( type == SMDSEntity_Polyhedra )
9332         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9333       else if ( type == SMDSEntity_Hexagonal_Prism )
9334         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9335
9336       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9337
9338       SMDS_MeshVolume * NewVolume = 0;
9339       switch ( type )
9340       {
9341       case SMDSEntity_Tetra:
9342         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9343         break;
9344       case SMDSEntity_Hexa:
9345       case SMDSEntity_Quad_Hexa:
9346       case SMDSEntity_TriQuad_Hexa:
9347         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9349         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9350           if ( nodes[i]->NbInverseElements() == 0 )
9351             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9352         break;
9353       case SMDSEntity_Pyramid:
9354         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9355                                       nodes[3], nodes[4], id, theForce3d);
9356         break;
9357       case SMDSEntity_Penta:
9358       case SMDSEntity_Quad_Penta:
9359       case SMDSEntity_BiQuad_Penta:
9360         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9361                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9362         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9363           if ( nodes[i]->NbInverseElements() == 0 )
9364             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9365         break;
9366       case SMDSEntity_Hexagonal_Prism:
9367       default:
9368         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9369       }
9370       ReplaceElemInGroups(volume, NewVolume, meshDS);
9371     }
9372   }
9373
9374   if ( !theForce3d )
9375   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9376     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9377     // aHelper.FixQuadraticElements(myError);
9378     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9379   }
9380 }
9381
9382 //================================================================================
9383 /*!
9384  * \brief Makes given elements quadratic
9385  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9386  *  \param theElements - elements to make quadratic
9387  */
9388 //================================================================================
9389
9390 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9391                                           TIDSortedElemSet& theElements,
9392                                           const bool        theToBiQuad)
9393 {
9394   if ( theElements.empty() ) return;
9395
9396   // we believe that all theElements are of the same type
9397   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9398
9399   // get all nodes shared by theElements
9400   TIDSortedNodeSet allNodes;
9401   TIDSortedElemSet::iterator eIt = theElements.begin();
9402   for ( ; eIt != theElements.end(); ++eIt )
9403     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9404
9405   // complete theElements with elements of lower dim whose all nodes are in allNodes
9406
9407   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9408   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9409   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9410   for ( ; nIt != allNodes.end(); ++nIt )
9411   {
9412     const SMDS_MeshNode* n = *nIt;
9413     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9414     while ( invIt->more() )
9415     {
9416       const SMDS_MeshElement*      e = invIt->next();
9417       const SMDSAbs_ElementType type = e->GetType();
9418       if ( e->IsQuadratic() )
9419       {
9420         quadAdjacentElems[ type ].insert( e );
9421
9422         bool alreadyOK;
9423         switch ( e->GetEntityType() ) {
9424         case SMDSEntity_Quad_Triangle:
9425         case SMDSEntity_Quad_Quadrangle:
9426         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9427         case SMDSEntity_BiQuad_Triangle:
9428         case SMDSEntity_BiQuad_Quadrangle:
9429         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9430         default:                           alreadyOK = true;
9431         }
9432         if ( alreadyOK )
9433           continue;
9434       }
9435       if ( type >= elemType )
9436         continue; // same type or more complex linear element
9437
9438       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9439         continue; // e is already checked
9440
9441       // check nodes
9442       bool allIn = true;
9443       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9444       while ( nodeIt->more() && allIn )
9445         allIn = allNodes.count( nodeIt->next() );
9446       if ( allIn )
9447         theElements.insert(e );
9448     }
9449   }
9450
9451   SMESH_MesherHelper helper(*myMesh);
9452   helper.SetIsQuadratic( true );
9453   helper.SetIsBiQuadratic( theToBiQuad );
9454
9455   // add links of quadratic adjacent elements to the helper
9456
9457   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9458     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9459           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9460     {
9461       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9462     }
9463   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9464     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9465           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9466     {
9467       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9468     }
9469   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9470     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9471           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9472     {
9473       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9474     }
9475
9476   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9477
9478   SMESHDS_Mesh*  meshDS = GetMeshDS();
9479   SMESHDS_SubMesh* smDS = 0;
9480   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9481   {
9482     const SMDS_MeshElement* elem = *eIt;
9483
9484     bool alreadyOK;
9485     int nbCentralNodes = 0;
9486     switch ( elem->GetEntityType() ) {
9487       // linear convertible
9488     case SMDSEntity_Edge:
9489     case SMDSEntity_Triangle:
9490     case SMDSEntity_Quadrangle:
9491     case SMDSEntity_Tetra:
9492     case SMDSEntity_Pyramid:
9493     case SMDSEntity_Hexa:
9494     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9495       // quadratic that can become bi-quadratic
9496     case SMDSEntity_Quad_Triangle:
9497     case SMDSEntity_Quad_Quadrangle:
9498     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9499       // bi-quadratic
9500     case SMDSEntity_BiQuad_Triangle:
9501     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9502     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9503       // the rest
9504     default:                           alreadyOK = true;
9505     }
9506     if ( alreadyOK ) continue;
9507
9508     const SMDSAbs_ElementType type = elem->GetType();
9509     const smIdType              id = elem->GetID();
9510     const int              nbNodes = elem->NbCornerNodes();
9511     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9512
9513     helper.SetSubShape( elem->getshapeId() );
9514
9515     if ( !smDS || !smDS->Contains( elem ))
9516       smDS = meshDS->MeshElements( elem->getshapeId() );
9517     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9518
9519     SMDS_MeshElement * newElem = 0;
9520     switch( nbNodes )
9521     {
9522     case 4: // cases for most frequently used element types go first (for optimization)
9523       if ( type == SMDSAbs_Volume )
9524         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9525       else
9526         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9527       break;
9528     case 8:
9529       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9530                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9531       break;
9532     case 3:
9533       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9534       break;
9535     case 2:
9536       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9537       break;
9538     case 5:
9539       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9540                                  nodes[4], id, theForce3d);
9541       break;
9542     case 6:
9543       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9544                                  nodes[4], nodes[5], id, theForce3d);
9545       break;
9546     default:;
9547     }
9548     ReplaceElemInGroups( elem, newElem, meshDS);
9549     if( newElem && smDS )
9550       smDS->AddElement( newElem );
9551
9552     // remove central nodes
9553     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9554       if ( nodes[i]->NbInverseElements() == 0 )
9555         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9556
9557   } // loop on theElements
9558
9559   if ( !theForce3d )
9560   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9561     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9562     // helper.FixQuadraticElements( myError );
9563     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9564   }
9565 }
9566
9567 //=======================================================================
9568 /*!
9569  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9570  * \return smIdType - nb of checked elements
9571  */
9572 //=======================================================================
9573
9574 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9575                                           SMDS_ElemIteratorPtr theItr,
9576                                           const int            /*theShapeID*/)
9577 {
9578   smIdType nbElem = 0;
9579   SMESHDS_Mesh* meshDS = GetMeshDS();
9580   ElemFeatures elemType;
9581   vector<const SMDS_MeshNode *> nodes;
9582
9583   while( theItr->more() )
9584   {
9585     const SMDS_MeshElement* elem = theItr->next();
9586     nbElem++;
9587     if( elem && elem->IsQuadratic())
9588     {
9589       // get elem data
9590       int nbCornerNodes = elem->NbCornerNodes();
9591       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9592
9593       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9594
9595       //remove a quadratic element
9596       if ( !theSm || !theSm->Contains( elem ))
9597         theSm = meshDS->MeshElements( elem->getshapeId() );
9598       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9599
9600       // remove medium nodes
9601       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9602         if ( nodes[i]->NbInverseElements() == 0 )
9603           meshDS->RemoveFreeNode( nodes[i], theSm );
9604
9605       // add a linear element
9606       nodes.resize( nbCornerNodes );
9607       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9608       ReplaceElemInGroups(elem, newElem, meshDS);
9609       if( theSm && newElem )
9610         theSm->AddElement( newElem );
9611     }
9612   }
9613   return nbElem;
9614 }
9615
9616 //=======================================================================
9617 //function : ConvertFromQuadratic
9618 //purpose  :
9619 //=======================================================================
9620
9621 bool SMESH_MeshEditor::ConvertFromQuadratic()
9622 {
9623   smIdType nbCheckedElems = 0;
9624   if ( myMesh->HasShapeToMesh() )
9625   {
9626     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9627     {
9628       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9629       while ( smIt->more() ) {
9630         SMESH_subMesh* sm = smIt->next();
9631         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9632           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9633       }
9634     }
9635   }
9636
9637   smIdType totalNbElems =
9638     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9639   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9640   {
9641     SMESHDS_SubMesh *aSM = 0;
9642     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9643   }
9644
9645   return true;
9646 }
9647
9648 namespace
9649 {
9650   //================================================================================
9651   /*!
9652    * \brief Return true if all medium nodes of the element are in the node set
9653    */
9654   //================================================================================
9655
9656   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9657   {
9658     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9659       if ( !nodeSet.count( elem->GetNode(i) ))
9660         return false;
9661     return true;
9662   }
9663 }
9664
9665 //================================================================================
9666 /*!
9667  * \brief Makes given elements linear
9668  */
9669 //================================================================================
9670
9671 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9672 {
9673   if ( theElements.empty() ) return;
9674
9675   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9676   set<smIdType> mediumNodeIDs;
9677   TIDSortedElemSet::iterator eIt = theElements.begin();
9678   for ( ; eIt != theElements.end(); ++eIt )
9679   {
9680     const SMDS_MeshElement* e = *eIt;
9681     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9682       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9683   }
9684
9685   // replace given elements by linear ones
9686   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9687   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9688
9689   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9690   // except those elements sharing medium nodes of quadratic element whose medium nodes
9691   // are not all in mediumNodeIDs
9692
9693   // get remaining medium nodes
9694   TIDSortedNodeSet mediumNodes;
9695   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9696   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9697     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9698       mediumNodes.insert( mediumNodes.end(), n );
9699
9700   // find more quadratic elements to convert
9701   TIDSortedElemSet moreElemsToConvert;
9702   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9703   for ( ; nIt != mediumNodes.end(); ++nIt )
9704   {
9705     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9706     while ( invIt->more() )
9707     {
9708       const SMDS_MeshElement* e = invIt->next();
9709       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9710       {
9711         // find a more complex element including e and
9712         // whose medium nodes are not in mediumNodes
9713         bool complexFound = false;
9714         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9715         {
9716           SMDS_ElemIteratorPtr invIt2 =
9717             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9718           while ( invIt2->more() )
9719           {
9720             const SMDS_MeshElement* eComplex = invIt2->next();
9721             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9722             {
9723               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9724               if ( nbCommonNodes == e->NbNodes())
9725               {
9726                 complexFound = true;
9727                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9728                 break;
9729               }
9730             }
9731           }
9732         }
9733         if ( !complexFound )
9734           moreElemsToConvert.insert( e );
9735       }
9736     }
9737   }
9738   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9739   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9740 }
9741
9742 //=======================================================================
9743 //function : SewSideElements
9744 //purpose  :
9745 //=======================================================================
9746
9747 SMESH_MeshEditor::Sew_Error
9748 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9749                                    TIDSortedElemSet&    theSide2,
9750                                    const SMDS_MeshNode* theFirstNode1,
9751                                    const SMDS_MeshNode* theFirstNode2,
9752                                    const SMDS_MeshNode* theSecondNode1,
9753                                    const SMDS_MeshNode* theSecondNode2)
9754 {
9755   ClearLastCreated();
9756
9757   if ( theSide1.size() != theSide2.size() )
9758     return SEW_DIFF_NB_OF_ELEMENTS;
9759
9760   Sew_Error aResult = SEW_OK;
9761   // Algo:
9762   // 1. Build set of faces representing each side
9763   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9764   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9765
9766   // =======================================================================
9767   // 1. Build set of faces representing each side:
9768   // =======================================================================
9769   // a. build set of nodes belonging to faces
9770   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9771   // c. create temporary faces representing side of volumes if correspondent
9772   //    face does not exist
9773
9774   SMESHDS_Mesh* aMesh = GetMeshDS();
9775   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9776   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9777   TIDSortedElemSet             faceSet1, faceSet2;
9778   set<const SMDS_MeshElement*> volSet1,  volSet2;
9779   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9780   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9781   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9782   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9783   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9784   int iSide, iFace, iNode;
9785
9786   list<const SMDS_MeshElement* > tempFaceList;
9787   for ( iSide = 0; iSide < 2; iSide++ ) {
9788     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9789     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9790     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9791     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9792     set<const SMDS_MeshElement*>::iterator vIt;
9793     TIDSortedElemSet::iterator eIt;
9794     set<const SMDS_MeshNode*>::iterator    nIt;
9795
9796     // check that given nodes belong to given elements
9797     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9798     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9799     int firstIndex = -1, secondIndex = -1;
9800     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9801       const SMDS_MeshElement* elem = *eIt;
9802       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9803       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9804       if ( firstIndex > -1 && secondIndex > -1 ) break;
9805     }
9806     if ( firstIndex < 0 || secondIndex < 0 ) {
9807       // we can simply return until temporary faces created
9808       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9809     }
9810
9811     // -----------------------------------------------------------
9812     // 1a. Collect nodes of existing faces
9813     //     and build set of face nodes in order to detect missing
9814     //     faces corresponding to sides of volumes
9815     // -----------------------------------------------------------
9816
9817     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9818
9819     // loop on the given element of a side
9820     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9821       //const SMDS_MeshElement* elem = *eIt;
9822       const SMDS_MeshElement* elem = *eIt;
9823       if ( elem->GetType() == SMDSAbs_Face ) {
9824         faceSet->insert( elem );
9825         set <const SMDS_MeshNode*> faceNodeSet;
9826         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9827         while ( nodeIt->more() ) {
9828           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9829           nodeSet->insert( n );
9830           faceNodeSet.insert( n );
9831         }
9832         setOfFaceNodeSet.insert( faceNodeSet );
9833       }
9834       else if ( elem->GetType() == SMDSAbs_Volume )
9835         volSet->insert( elem );
9836     }
9837     // ------------------------------------------------------------------------------
9838     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9839     // ------------------------------------------------------------------------------
9840
9841     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9842       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9843       while ( fIt->more() ) { // loop on faces sharing a node
9844         const SMDS_MeshElement* f = fIt->next();
9845         if ( faceSet->find( f ) == faceSet->end() ) {
9846           // check if all nodes are in nodeSet and
9847           // complete setOfFaceNodeSet if they are
9848           set <const SMDS_MeshNode*> faceNodeSet;
9849           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9850           bool allInSet = true;
9851           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9852             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9853             if ( nodeSet->find( n ) == nodeSet->end() )
9854               allInSet = false;
9855             else
9856               faceNodeSet.insert( n );
9857           }
9858           if ( allInSet ) {
9859             faceSet->insert( f );
9860             setOfFaceNodeSet.insert( faceNodeSet );
9861           }
9862         }
9863       }
9864     }
9865
9866     // -------------------------------------------------------------------------
9867     // 1c. Create temporary faces representing sides of volumes if correspondent
9868     //     face does not exist
9869     // -------------------------------------------------------------------------
9870
9871     if ( !volSet->empty() ) {
9872       //int nodeSetSize = nodeSet->size();
9873
9874       // loop on given volumes
9875       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9876         SMDS_VolumeTool vol (*vIt);
9877         // loop on volume faces: find free faces
9878         // --------------------------------------
9879         list<const SMDS_MeshElement* > freeFaceList;
9880         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9881           if ( !vol.IsFreeFace( iFace ))
9882             continue;
9883           // check if there is already a face with same nodes in a face set
9884           const SMDS_MeshElement* aFreeFace = 0;
9885           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9886           int nbNodes = vol.NbFaceNodes( iFace );
9887           set <const SMDS_MeshNode*> faceNodeSet;
9888           vol.GetFaceNodes( iFace, faceNodeSet );
9889           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9890           if ( isNewFace ) {
9891             // no such a face is given but it still can exist, check it
9892             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9893             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9894           }
9895           if ( !aFreeFace ) {
9896             // create a temporary face
9897             if ( nbNodes == 3 ) {
9898               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9899               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9900             }
9901             else if ( nbNodes == 4 ) {
9902               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9903               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9904             }
9905             else {
9906               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9907               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9908               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9909             }
9910             if ( aFreeFace )
9911               tempFaceList.push_back( aFreeFace );
9912           }
9913
9914           if ( aFreeFace )
9915             freeFaceList.push_back( aFreeFace );
9916
9917         } // loop on faces of a volume
9918
9919         // choose one of several free faces of a volume
9920         // --------------------------------------------
9921         if ( freeFaceList.size() > 1 ) {
9922           // choose a face having max nb of nodes shared by other elems of a side
9923           int maxNbNodes = -1;
9924           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9925           while ( fIt != freeFaceList.end() ) { // loop on free faces
9926             int nbSharedNodes = 0;
9927             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9928             while ( nodeIt->more() ) { // loop on free face nodes
9929               const SMDS_MeshNode* n =
9930                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9931               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9932               while ( invElemIt->more() ) {
9933                 const SMDS_MeshElement* e = invElemIt->next();
9934                 nbSharedNodes += faceSet->count( e );
9935                 nbSharedNodes += elemSet->count( e );
9936               }
9937             }
9938             if ( nbSharedNodes > maxNbNodes ) {
9939               maxNbNodes = nbSharedNodes;
9940               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9941             }
9942             else if ( nbSharedNodes == maxNbNodes ) {
9943               fIt++;
9944             }
9945             else {
9946               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9947             }
9948           }
9949           if ( freeFaceList.size() > 1 )
9950           {
9951             // could not choose one face, use another way
9952             // choose a face most close to the bary center of the opposite side
9953             gp_XYZ aBC( 0., 0., 0. );
9954             set <const SMDS_MeshNode*> addedNodes;
9955             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9956             eIt = elemSet2->begin();
9957             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9958               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9959               while ( nodeIt->more() ) { // loop on free face nodes
9960                 const SMDS_MeshNode* n =
9961                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9962                 if ( addedNodes.insert( n ).second )
9963                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9964               }
9965             }
9966             aBC /= addedNodes.size();
9967             double minDist = DBL_MAX;
9968             fIt = freeFaceList.begin();
9969             while ( fIt != freeFaceList.end() ) { // loop on free faces
9970               double dist = 0;
9971               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9972               while ( nodeIt->more() ) { // loop on free face nodes
9973                 const SMDS_MeshNode* n =
9974                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9975                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9976                 dist += ( aBC - p ).SquareModulus();
9977               }
9978               if ( dist < minDist ) {
9979                 minDist = dist;
9980                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9981               }
9982               else
9983                 fIt = freeFaceList.erase( fIt++ );
9984             }
9985           }
9986         } // choose one of several free faces of a volume
9987
9988         if ( freeFaceList.size() == 1 ) {
9989           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9990           faceSet->insert( aFreeFace );
9991           // complete a node set with nodes of a found free face
9992           //           for ( iNode = 0; iNode < ; iNode++ )
9993           //             nodeSet->insert( fNodes[ iNode ] );
9994         }
9995
9996       } // loop on volumes of a side
9997
9998       //       // complete a set of faces if new nodes in a nodeSet appeared
9999       //       // ----------------------------------------------------------
10000       //       if ( nodeSetSize != nodeSet->size() ) {
10001       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10002       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10003       //           while ( fIt->more() ) { // loop on faces sharing a node
10004       //             const SMDS_MeshElement* f = fIt->next();
10005       //             if ( faceSet->find( f ) == faceSet->end() ) {
10006       //               // check if all nodes are in nodeSet and
10007       //               // complete setOfFaceNodeSet if they are
10008       //               set <const SMDS_MeshNode*> faceNodeSet;
10009       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10010       //               bool allInSet = true;
10011       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10012       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10013       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10014       //                   allInSet = false;
10015       //                 else
10016       //                   faceNodeSet.insert( n );
10017       //               }
10018       //               if ( allInSet ) {
10019       //                 faceSet->insert( f );
10020       //                 setOfFaceNodeSet.insert( faceNodeSet );
10021       //               }
10022       //             }
10023       //           }
10024       //         }
10025       //       }
10026     } // Create temporary faces, if there are volumes given
10027   } // loop on sides
10028
10029   if ( faceSet1.size() != faceSet2.size() ) {
10030     // delete temporary faces: they are in reverseElements of actual nodes
10031     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10032     //    while ( tmpFaceIt->more() )
10033     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10034     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10035     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10036     //      aMesh->RemoveElement(*tmpFaceIt);
10037     MESSAGE("Diff nb of faces");
10038     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10039   }
10040
10041   // ============================================================
10042   // 2. Find nodes to merge:
10043   //              bind a node to remove to a node to put instead
10044   // ============================================================
10045
10046   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10047   if ( theFirstNode1 != theFirstNode2 )
10048     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10049   if ( theSecondNode1 != theSecondNode2 )
10050     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10051
10052   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10053   set< long > linkIdSet; // links to process
10054   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10055
10056   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10057   list< NLink > linkList[2];
10058   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10059   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10060   // loop on links in linkList; find faces by links and append links
10061   // of the found faces to linkList
10062   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10063   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10064   {
10065     NLink link[] = { *linkIt[0], *linkIt[1] };
10066     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10067     if ( !linkIdSet.count( linkID ) )
10068       continue;
10069
10070     // by links, find faces in the face sets,
10071     // and find indices of link nodes in the found faces;
10072     // in a face set, there is only one or no face sharing a link
10073     // ---------------------------------------------------------------
10074
10075     const SMDS_MeshElement* face[] = { 0, 0 };
10076     vector<const SMDS_MeshNode*> fnodes[2];
10077     int iLinkNode[2][2];
10078     TIDSortedElemSet avoidSet;
10079     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10080       const SMDS_MeshNode* n1 = link[iSide].first;
10081       const SMDS_MeshNode* n2 = link[iSide].second;
10082       //cout << "Side " << iSide << " ";
10083       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10084       // find a face by two link nodes
10085       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10086                                                       *faceSetPtr[ iSide ], avoidSet,
10087                                                       &iLinkNode[iSide][0],
10088                                                       &iLinkNode[iSide][1] );
10089       if ( face[ iSide ])
10090       {
10091         //cout << " F " << face[ iSide]->GetID() <<endl;
10092         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10093         // put face nodes to fnodes
10094         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10095         fnodes[ iSide ].assign( nIt, nEnd );
10096         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10097       }
10098     }
10099
10100     // check similarity of elements of the sides
10101     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10102       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10103       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10104         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10105       }
10106       else {
10107         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10108       }
10109       break; // do not return because it's necessary to remove tmp faces
10110     }
10111
10112     // set nodes to merge
10113     // -------------------
10114
10115     if ( face[0] && face[1] )  {
10116       const int nbNodes = face[0]->NbNodes();
10117       if ( nbNodes != face[1]->NbNodes() ) {
10118         MESSAGE("Diff nb of face nodes");
10119         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10120         break; // do not return because it s necessary to remove tmp faces
10121       }
10122       bool reverse[] = { false, false }; // order of nodes in the link
10123       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10124         // analyse link orientation in faces
10125         int i1 = iLinkNode[ iSide ][ 0 ];
10126         int i2 = iLinkNode[ iSide ][ 1 ];
10127         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10128       }
10129       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10130       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10131       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10132       {
10133         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10134                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10135       }
10136
10137       // add other links of the faces to linkList
10138       // -----------------------------------------
10139
10140       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10141         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10142         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10143         if ( !iter_isnew.second ) { // already in a set: no need to process
10144           linkIdSet.erase( iter_isnew.first );
10145         }
10146         else // new in set == encountered for the first time: add
10147         {
10148           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10149           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10150           linkList[0].push_back ( NLink( n1, n2 ));
10151           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10152         }
10153       }
10154     } // 2 faces found
10155
10156     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10157       break;
10158
10159   } // loop on link lists
10160
10161   if ( aResult == SEW_OK &&
10162        ( //linkIt[0] != linkList[0].end() ||
10163         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10164     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10165              " " << (faceSetPtr[1]->empty()));
10166     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10167   }
10168
10169   // ====================================================================
10170   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10171   // ====================================================================
10172
10173   // delete temporary faces
10174   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10175   //  while ( tmpFaceIt->more() )
10176   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10177   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10178   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10179     aMesh->RemoveElement(*tmpFaceIt);
10180
10181   if ( aResult != SEW_OK)
10182     return aResult;
10183
10184   list< smIdType > nodeIDsToRemove;
10185   vector< const SMDS_MeshNode*> nodes;
10186   ElemFeatures elemType;
10187
10188   // loop on nodes replacement map
10189   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10190   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10191     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10192     {
10193       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10194       nodeIDsToRemove.push_back( nToRemove->GetID() );
10195       // loop on elements sharing nToRemove
10196       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10197       while ( invElemIt->more() ) {
10198         const SMDS_MeshElement* e = invElemIt->next();
10199         // get a new suite of nodes: make replacement
10200         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10201         nodes.resize( nbNodes );
10202         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10203         while ( nIt->more() ) {
10204           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10205           nnIt = nReplaceMap.find( n );
10206           if ( nnIt != nReplaceMap.end() ) {
10207             nbReplaced++;
10208             n = (*nnIt).second;
10209           }
10210           nodes[ i++ ] = n;
10211         }
10212         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10213         //         elemIDsToRemove.push_back( e->GetID() );
10214         //       else
10215         if ( nbReplaced )
10216         {
10217           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10218           aMesh->RemoveElement( e );
10219
10220           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10221           {
10222             AddToSameGroups( newElem, e, aMesh );
10223             if ( int aShapeId = e->getshapeId() )
10224               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10225           }
10226         }
10227       }
10228     }
10229
10230   Remove( nodeIDsToRemove, true );
10231
10232   return aResult;
10233 }
10234
10235 //================================================================================
10236 /*!
10237  * \brief Find corresponding nodes in two sets of faces
10238  * \param theSide1 - first face set
10239  * \param theSide2 - second first face
10240  * \param theFirstNode1 - a boundary node of set 1
10241  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10242  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10243  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10244  * \param nReplaceMap - output map of corresponding nodes
10245  * \return bool  - is a success or not
10246  */
10247 //================================================================================
10248
10249 #ifdef _DEBUG_
10250 //#define DEBUG_MATCHING_NODES
10251 #endif
10252
10253 SMESH_MeshEditor::Sew_Error
10254 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10255                                     set<const SMDS_MeshElement*>& theSide2,
10256                                     const SMDS_MeshNode*          theFirstNode1,
10257                                     const SMDS_MeshNode*          theFirstNode2,
10258                                     const SMDS_MeshNode*          theSecondNode1,
10259                                     const SMDS_MeshNode*          theSecondNode2,
10260                                     TNodeNodeMap &                nReplaceMap)
10261 {
10262   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10263
10264   nReplaceMap.clear();
10265   //if ( theFirstNode1 != theFirstNode2 )
10266   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10267   //if ( theSecondNode1 != theSecondNode2 )
10268   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10269
10270   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10271   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10272
10273   list< NLink > linkList[2];
10274   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10275   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10276
10277   // loop on links in linkList; find faces by links and append links
10278   // of the found faces to linkList
10279   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10280   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10281     NLink link[] = { *linkIt[0], *linkIt[1] };
10282     if ( linkSet.find( link[0] ) == linkSet.end() )
10283       continue;
10284
10285     // by links, find faces in the face sets,
10286     // and find indices of link nodes in the found faces;
10287     // in a face set, there is only one or no face sharing a link
10288     // ---------------------------------------------------------------
10289
10290     const SMDS_MeshElement* face[] = { 0, 0 };
10291     list<const SMDS_MeshNode*> notLinkNodes[2];
10292     //bool reverse[] = { false, false }; // order of notLinkNodes
10293     int nbNodes[2];
10294     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10295     {
10296       const SMDS_MeshNode* n1 = link[iSide].first;
10297       const SMDS_MeshNode* n2 = link[iSide].second;
10298       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10299       set< const SMDS_MeshElement* > facesOfNode1;
10300       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10301       {
10302         // during a loop of the first node, we find all faces around n1,
10303         // during a loop of the second node, we find one face sharing both n1 and n2
10304         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10305         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10306         while ( fIt->more() ) { // loop on faces sharing a node
10307           const SMDS_MeshElement* f = fIt->next();
10308           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10309               ! facesOfNode1.insert( f ).second ) // f encounters twice
10310           {
10311             if ( face[ iSide ] ) {
10312               MESSAGE( "2 faces per link " );
10313               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10314             }
10315             face[ iSide ] = f;
10316             faceSet->erase( f );
10317
10318             // get not link nodes
10319             int nbN = f->NbNodes();
10320             if ( f->IsQuadratic() )
10321               nbN /= 2;
10322             nbNodes[ iSide ] = nbN;
10323             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10324             int i1 = f->GetNodeIndex( n1 );
10325             int i2 = f->GetNodeIndex( n2 );
10326             int iEnd = nbN, iBeg = -1, iDelta = 1;
10327             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10328             if ( reverse ) {
10329               std::swap( iEnd, iBeg ); iDelta = -1;
10330             }
10331             int i = i2;
10332             while ( true ) {
10333               i += iDelta;
10334               if ( i == iEnd ) i = iBeg + iDelta;
10335               if ( i == i1 ) break;
10336               nodes.push_back ( f->GetNode( i ) );
10337             }
10338           }
10339         }
10340       }
10341     }
10342     // check similarity of elements of the sides
10343     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10344       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10345       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10346         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10347       }
10348       else {
10349         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10350       }
10351     }
10352
10353     // set nodes to merge
10354     // -------------------
10355
10356     if ( face[0] && face[1] )  {
10357       if ( nbNodes[0] != nbNodes[1] ) {
10358         MESSAGE("Diff nb of face nodes");
10359         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10360       }
10361 #ifdef DEBUG_MATCHING_NODES
10362       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10363                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10364                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10365 #endif
10366       int nbN = nbNodes[0];
10367       {
10368         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10369         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10370         for ( int i = 0 ; i < nbN - 2; ++i ) {
10371 #ifdef DEBUG_MATCHING_NODES
10372           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10373 #endif
10374           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10375         }
10376       }
10377
10378       // add other links of the face 1 to linkList
10379       // -----------------------------------------
10380
10381       const SMDS_MeshElement* f0 = face[0];
10382       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10383       for ( int i = 0; i < nbN; i++ )
10384       {
10385         const SMDS_MeshNode* n2 = f0->GetNode( i );
10386         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10387           linkSet.insert( SMESH_TLink( n1, n2 ));
10388         if ( !iter_isnew.second ) { // already in a set: no need to process
10389           linkSet.erase( iter_isnew.first );
10390         }
10391         else // new in set == encountered for the first time: add
10392         {
10393 #ifdef DEBUG_MATCHING_NODES
10394           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10395                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10396 #endif
10397           linkList[0].push_back ( NLink( n1, n2 ));
10398           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10399         }
10400         n1 = n2;
10401       }
10402     } // 2 faces found
10403   } // loop on link lists
10404
10405   return SEW_OK;
10406 }
10407
10408 namespace // automatically find theAffectedElems for DoubleNodes()
10409 {
10410   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10411
10412   //--------------------------------------------------------------------------------
10413   // Nodes shared by adjacent FissureBorder's.
10414   // 1 node  if FissureBorder separates faces
10415   // 2 nodes if FissureBorder separates volumes
10416   struct SubBorder
10417   {
10418     const SMDS_MeshNode* _nodes[2];
10419     int                  _nbNodes;
10420
10421     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10422     {
10423       _nodes[0] = n1;
10424       _nodes[1] = n2;
10425       _nbNodes = bool( n1 ) + bool( n2 );
10426       if ( _nbNodes == 2 && n1 > n2 )
10427         std::swap( _nodes[0], _nodes[1] );
10428     }
10429     bool operator<( const SubBorder& other ) const
10430     {
10431       for ( int i = 0; i < _nbNodes; ++i )
10432       {
10433         if ( _nodes[i] < other._nodes[i] ) return true;
10434         if ( _nodes[i] > other._nodes[i] ) return false;
10435       }
10436       return false;
10437     }
10438   };
10439
10440   //--------------------------------------------------------------------------------
10441   // Map a SubBorder to all FissureBorder it bounds
10442   struct FissureBorder;
10443   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10444   typedef TBorderLinks::iterator                               TMappedSub;
10445
10446   //--------------------------------------------------------------------------------
10447   /*!
10448    * \brief Element border (volume facet or face edge) at a fissure
10449    */
10450   struct FissureBorder
10451   {
10452     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10453     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10454
10455     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10456     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10457
10458     FissureBorder( FissureBorder && from ) // move constructor
10459     {
10460       std::swap( _nodes,       from._nodes );
10461       std::swap( _sortedNodes, from._sortedNodes );
10462       _elems[0] = from._elems[0];
10463       _elems[1] = from._elems[1];
10464     }
10465
10466     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10467                    std::vector< const SMDS_MeshElement* > & adjElems)
10468       : _nodes( elemToDuplicate->NbCornerNodes() )
10469     {
10470       for ( size_t i = 0; i < _nodes.size(); ++i )
10471         _nodes[i] = elemToDuplicate->GetNode( i );
10472
10473       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10474       findAdjacent( type, adjElems );
10475     }
10476
10477     FissureBorder( const SMDS_MeshNode**                    nodes,
10478                    const size_t                             nbNodes,
10479                    const SMDSAbs_ElementType                adjElemsType,
10480                    std::vector< const SMDS_MeshElement* > & adjElems)
10481       : _nodes( nodes, nodes + nbNodes )
10482     {
10483       findAdjacent( adjElemsType, adjElems );
10484     }
10485
10486     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10487                        std::vector< const SMDS_MeshElement* > & adjElems)
10488     {
10489       _elems[0] = _elems[1] = 0;
10490       adjElems.clear();
10491       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10492         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10493           _elems[i] = adjElems[i];
10494     }
10495
10496     bool operator<( const FissureBorder& other ) const
10497     {
10498       return GetSortedNodes() < other.GetSortedNodes();
10499     }
10500
10501     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10502     {
10503       if ( _sortedNodes.empty() && !_nodes.empty() )
10504       {
10505         FissureBorder* me = const_cast<FissureBorder*>( this );
10506         me->_sortedNodes = me->_nodes;
10507         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10508       }
10509       return _sortedNodes;
10510     }
10511
10512     size_t NbSub() const
10513     {
10514       return _nodes.size();
10515     }
10516
10517     SubBorder Sub(size_t i) const
10518     {
10519       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10520     }
10521
10522     void AddSelfTo( TBorderLinks& borderLinks )
10523     {
10524       _mappedSubs.resize( NbSub() );
10525       for ( size_t i = 0; i < NbSub(); ++i )
10526       {
10527         TBorderLinks::iterator s2b =
10528           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10529         s2b->second.push_back( this );
10530         _mappedSubs[ i ] = s2b;
10531       }
10532     }
10533
10534     void Clear()
10535     {
10536       _nodes.clear();
10537     }
10538
10539     const SMDS_MeshElement* GetMarkedElem() const
10540     {
10541       if ( _nodes.empty() ) return 0; // cleared
10542       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10543       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10544       return 0;
10545     }
10546
10547     gp_XYZ GetNorm() const // normal to the border
10548     {
10549       gp_XYZ norm;
10550       if ( _nodes.size() == 2 )
10551       {
10552         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10553         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10554           avgNorm += norm;
10555         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10556           avgNorm += norm;
10557
10558         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10559         norm = bordDir ^ avgNorm;
10560       }
10561       else
10562       {
10563         SMESH_NodeXYZ p0( _nodes[0] );
10564         SMESH_NodeXYZ p1( _nodes[1] );
10565         SMESH_NodeXYZ p2( _nodes[2] );
10566         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10567       }
10568       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10569         norm.Reverse();
10570
10571       return norm;
10572     }
10573
10574     void ChooseSide() // mark an _elem located at positive side of fissure
10575     {
10576       _elems[0]->setIsMarked( true );
10577       gp_XYZ norm = GetNorm();
10578       double maxX = norm.Coord(1);
10579       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10580       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10581       if ( maxX < 0 )
10582       {
10583         _elems[0]->setIsMarked( false );
10584         if ( _elems[1] )
10585           _elems[1]->setIsMarked( true );
10586       }
10587     }
10588
10589   }; // struct FissureBorder
10590
10591   //--------------------------------------------------------------------------------
10592   /*!
10593    * \brief Classifier of elements at fissure edge
10594    */
10595   class FissureNormal
10596   {
10597     std::vector< gp_XYZ > _normals;
10598     bool                  _bothIn;
10599
10600   public:
10601     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10602     {
10603       _bothIn = false;
10604       _normals.reserve(2);
10605       _normals.push_back( bord.GetNorm() );
10606       if ( _normals.size() == 2 )
10607         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10608     }
10609
10610     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10611     {
10612       bool isIn = false;
10613       switch ( _normals.size() ) {
10614       case 1:
10615       {
10616         isIn = !isOut( n, _normals[0], elem );
10617         break;
10618       }
10619       case 2:
10620       {
10621         bool in1 = !isOut( n, _normals[0], elem );
10622         bool in2 = !isOut( n, _normals[1], elem );
10623         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10624       }
10625       }
10626       return isIn;
10627     }
10628   };
10629
10630   //================================================================================
10631   /*!
10632    * \brief Classify an element by a plane passing through a node
10633    */
10634   //================================================================================
10635
10636   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10637   {
10638     SMESH_NodeXYZ p = n;
10639     double sumDot = 0;
10640     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10641     {
10642       SMESH_NodeXYZ pi = elem->GetNode( i );
10643       sumDot += norm * ( pi - p );
10644     }
10645     return sumDot < -1e-100;
10646   }
10647
10648   //================================================================================
10649   /*!
10650    * \brief Find FissureBorder's by nodes to duplicate
10651    */
10652   //================================================================================
10653
10654   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10655                            std::vector< FissureBorder > & theFissureBorders )
10656   {
10657     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10658     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10659     if ( !n ) return;
10660     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10661     if ( n->NbInverseElements( elemType ) == 0 )
10662     {
10663       elemType = SMDSAbs_Face;
10664       if ( n->NbInverseElements( elemType ) == 0 )
10665         return;
10666     }
10667     // unmark elements touching the fissure
10668     for ( ; nIt != theNodes.end(); ++nIt )
10669       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10670
10671     // loop on elements touching the fissure to get their borders belonging to the fissure
10672     std::set< FissureBorder >              fissureBorders;
10673     std::vector< const SMDS_MeshElement* > adjElems;
10674     std::vector< const SMDS_MeshNode* >    nodes;
10675     SMDS_VolumeTool volTool;
10676     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10677     {
10678       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10679       while ( invIt->more() )
10680       {
10681         const SMDS_MeshElement* eInv = invIt->next();
10682         if ( eInv->isMarked() ) continue;
10683         eInv->setIsMarked( true );
10684
10685         if ( elemType == SMDSAbs_Volume )
10686         {
10687           volTool.Set( eInv );
10688           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10689           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10690           {
10691             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10692             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10693             nodes.clear();
10694             bool allOnFissure = true;
10695             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10696               if (( allOnFissure = theNodes.count( nn[ iN ])))
10697                 nodes.push_back( nn[ iN ]);
10698             if ( allOnFissure )
10699               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10700                                                                elemType, adjElems )));
10701           }
10702         }
10703         else // elemType == SMDSAbs_Face
10704         {
10705           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10706           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10707           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10708           {
10709             nn[1]      = eInv->GetNode( iN );
10710             onFissure1 = theNodes.count( nn[1] );
10711             if ( onFissure0 && onFissure1 )
10712               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10713             nn[0]      = nn[1];
10714             onFissure0 = onFissure1;
10715           }
10716         }
10717       }
10718     }
10719
10720     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10721     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10722     for ( ; bord != fissureBorders.end(); ++bord )
10723     {
10724       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10725     }
10726     return;
10727   } // findFissureBorders()
10728
10729   //================================================================================
10730   /*!
10731    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10732    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10733    *  \param [in] theNodesNot - nodes not to duplicate
10734    *  \param [out] theAffectedElems - the found elements
10735    */
10736   //================================================================================
10737
10738   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10739                           TIDSortedElemSet&       theAffectedElems)
10740   {
10741     if ( theElemsOrNodes.empty() ) return;
10742
10743     // find FissureBorder's
10744
10745     std::vector< FissureBorder >           fissure;
10746     std::vector< const SMDS_MeshElement* > elemsByFacet;
10747
10748     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10749     if ( (*elIt)->GetType() == SMDSAbs_Node )
10750     {
10751       findFissureBorders( theElemsOrNodes, fissure );
10752     }
10753     else
10754     {
10755       fissure.reserve( theElemsOrNodes.size() );
10756       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10757       {
10758         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10759         if ( !fissure.back()._elems[1] )
10760           fissure.pop_back();
10761       }
10762     }
10763     if ( fissure.empty() )
10764       return;
10765
10766     // fill borderLinks
10767
10768     TBorderLinks borderLinks;
10769
10770     for ( size_t i = 0; i < fissure.size(); ++i )
10771     {
10772       fissure[i].AddSelfTo( borderLinks );
10773     }
10774
10775     // get theAffectedElems
10776
10777     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10778     for ( size_t i = 0; i < fissure.size(); ++i )
10779       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10780       {
10781         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10782                                         false, /*markElem=*/true );
10783       }
10784
10785     std::vector<const SMDS_MeshNode *>                 facetNodes;
10786     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10787     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10788
10789     // choose a side of fissure
10790     fissure[0].ChooseSide();
10791     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10792
10793     size_t nbCheckedBorders = 0;
10794     while ( nbCheckedBorders < fissure.size() )
10795     {
10796       // find a FissureBorder to treat
10797       FissureBorder* bord = 0;
10798       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10799         if ( fissure[i].GetMarkedElem() )
10800           bord = & fissure[i];
10801       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10802         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10803         {
10804           bord = & fissure[i];
10805           bord->ChooseSide();
10806           theAffectedElems.insert( bord->GetMarkedElem() );
10807         }
10808       if ( !bord ) return;
10809       ++nbCheckedBorders;
10810
10811       // treat FissureBorder's linked to bord
10812       fissureNodes.clear();
10813       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10814       for ( size_t i = 0; i < bord->NbSub(); ++i )
10815       {
10816         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10817         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10818         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10819         const SubBorder&                          sb = l2b->first;
10820         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10821
10822         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10823         {
10824           for ( int j = 0; j < sb._nbNodes; ++j )
10825             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10826           continue;
10827         }
10828
10829         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10830         // until an elem adjacent to a neighbour FissureBorder is found
10831         facetNodes.clear();
10832         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10833         facetNodes.resize( sb._nbNodes + 1 );
10834
10835         while ( bordElem )
10836         {
10837           // check if bordElem is adjacent to a neighbour FissureBorder
10838           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10839           {
10840             FissureBorder* bord2 = linkedBorders[j];
10841             if ( bord2 == bord ) continue;
10842             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10843               bordElem = 0;
10844             else
10845               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10846           }
10847           if ( !bordElem )
10848             break;
10849
10850           // find the next bordElem
10851           const SMDS_MeshElement* nextBordElem = 0;
10852           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10853           {
10854             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10855             if ( fissureNodes.count( n )) continue;
10856
10857             facetNodes[ sb._nbNodes ] = n;
10858             elemsByFacet.clear();
10859             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10860             {
10861               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10862                 if ( elemsByFacet[ iE ] != bordElem &&
10863                      !elemsByFacet[ iE ]->isMarked() )
10864                 {
10865                   theAffectedElems.insert( elemsByFacet[ iE ]);
10866                   elemsByFacet[ iE ]->setIsMarked( true );
10867                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10868                     nextBordElem = elemsByFacet[ iE ];
10869                 }
10870             }
10871           }
10872           bordElem = nextBordElem;
10873
10874         } // while ( bordElem )
10875
10876         linkedBorders.clear(); // not to treat this link any more
10877
10878       } // loop on SubBorder's of a FissureBorder
10879
10880       bord->Clear();
10881
10882     } // loop on FissureBorder's
10883
10884
10885     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10886
10887     // mark nodes of theAffectedElems
10888     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10889
10890     // unmark nodes of the fissure
10891     elIt = theElemsOrNodes.begin();
10892     if ( (*elIt)->GetType() == SMDSAbs_Node )
10893       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10894     else
10895       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10896
10897     std::vector< gp_XYZ > normVec;
10898
10899     // loop on nodes of the fissure, add elements having marked nodes
10900     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10901     {
10902       const SMDS_MeshElement* e = (*elIt);
10903       if ( e->GetType() != SMDSAbs_Node )
10904         e->setIsMarked( true ); // avoid adding a fissure element
10905
10906       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10907       {
10908         const SMDS_MeshNode* n = e->GetNode( iN );
10909         if ( fissEdgeNodes2Norm.count( n ))
10910           continue;
10911
10912         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10913         while ( invIt->more() )
10914         {
10915           const SMDS_MeshElement* eInv = invIt->next();
10916           if ( eInv->isMarked() ) continue;
10917           eInv->setIsMarked( true );
10918
10919           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10920           while( nIt->more() )
10921             if ( nIt->next()->isMarked())
10922             {
10923               theAffectedElems.insert( eInv );
10924               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10925               n->setIsMarked( false );
10926               break;
10927             }
10928         }
10929       }
10930     }
10931
10932     // add elements on the fissure edge
10933     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10934     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10935     {
10936       const SMDS_MeshNode* edgeNode = n2N->first;
10937       const FissureNormal & normals = n2N->second;
10938
10939       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10940       while ( invIt->more() )
10941       {
10942         const SMDS_MeshElement* eInv = invIt->next();
10943         if ( eInv->isMarked() ) continue;
10944         eInv->setIsMarked( true );
10945
10946         // classify eInv using normals
10947         bool toAdd = normals.IsIn( edgeNode, eInv );
10948         if ( toAdd ) // check if all nodes lie on the fissure edge
10949         {
10950           bool notOnEdge = false;
10951           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10952             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10953           toAdd = notOnEdge;
10954         }
10955         if ( toAdd )
10956         {
10957           theAffectedElems.insert( eInv );
10958         }
10959       }
10960     }
10961
10962     return;
10963   } // findAffectedElems()
10964 } // namespace
10965
10966 //================================================================================
10967 /*!
10968  * \brief Create elements equal (on same nodes) to given ones
10969  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10970  *              elements of the uppest dimension are duplicated.
10971  */
10972 //================================================================================
10973
10974 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10975 {
10976   ClearLastCreated();
10977   SMESHDS_Mesh* mesh = GetMeshDS();
10978
10979   // get an element type and an iterator over elements
10980
10981   SMDSAbs_ElementType type = SMDSAbs_All;
10982   SMDS_ElemIteratorPtr elemIt;
10983   if ( theElements.empty() )
10984   {
10985     if ( mesh->NbNodes() == 0 )
10986       return;
10987     // get most complex type
10988     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10989       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10990       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10991     };
10992     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10993       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10994       {
10995         type = types[i];
10996         elemIt = mesh->elementsIterator( type );
10997         break;
10998       }
10999   }
11000   else
11001   {
11002     //type = (*theElements.begin())->GetType();
11003     elemIt = SMESHUtils::elemSetIterator( theElements );
11004   }
11005
11006   // un-mark all elements to avoid duplicating just created elements
11007   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11008
11009   // duplicate elements
11010
11011   ElemFeatures elemType;
11012
11013   vector< const SMDS_MeshNode* > nodes;
11014   while ( elemIt->more() )
11015   {
11016     const SMDS_MeshElement* elem = elemIt->next();
11017     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11018         ( elem->isMarked() ))
11019       continue;
11020
11021     elemType.Init( elem, /*basicOnly=*/false );
11022     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11023
11024     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11025       newElem->setIsMarked( true );
11026   }
11027 }
11028
11029 //================================================================================
11030 /*!
11031   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11032   \param theElems - the list of elements (edges or faces) to be replicated
11033   The nodes for duplication could be found from these elements
11034   \param theNodesNot - list of nodes to NOT replicate
11035   \param theAffectedElems - the list of elements (cells and edges) to which the
11036   replicated nodes should be associated to.
11037   \return TRUE if operation has been completed successfully, FALSE otherwise
11038 */
11039 //================================================================================
11040
11041 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11042                                     const TIDSortedElemSet& theNodesNot,
11043                                     const TIDSortedElemSet& theAffectedElems )
11044 {
11045   ClearLastCreated();
11046
11047   if ( theElems.size() == 0 )
11048     return false;
11049
11050   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11051   if ( !aMeshDS )
11052     return false;
11053
11054   bool res = false;
11055   TNodeNodeMap anOldNodeToNewNode;
11056   // duplicate elements and nodes
11057   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11058   // replce nodes by duplications
11059   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11060   return res;
11061 }
11062
11063 //================================================================================
11064 /*!
11065   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11066   \param theMeshDS - mesh instance
11067   \param theElems - the elements replicated or modified (nodes should be changed)
11068   \param theNodesNot - nodes to NOT replicate
11069   \param theNodeNodeMap - relation of old node to new created node
11070   \param theIsDoubleElem - flag os to replicate element or modify
11071   \return TRUE if operation has been completed successfully, FALSE otherwise
11072 */
11073 //================================================================================
11074
11075 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11076                                    const TIDSortedElemSet& theElems,
11077                                    const TIDSortedElemSet& theNodesNot,
11078                                    TNodeNodeMap&           theNodeNodeMap,
11079                                    const bool              theIsDoubleElem )
11080 {
11081   // iterate through element and duplicate them (by nodes duplication)
11082   bool res = false;
11083   std::vector<const SMDS_MeshNode*> newNodes;
11084   ElemFeatures elemType;
11085
11086   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11087   for ( ;  elemItr != theElems.end(); ++elemItr )
11088   {
11089     const SMDS_MeshElement* anElem = *elemItr;
11090     // if (!anElem)
11091     //   continue;
11092
11093     // duplicate nodes to duplicate element
11094     bool isDuplicate = false;
11095     newNodes.resize( anElem->NbNodes() );
11096     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11097     int ind = 0;
11098     while ( anIter->more() )
11099     {
11100       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11101       const SMDS_MeshNode*  aNewNode = aCurrNode;
11102       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11103       if ( n2n != theNodeNodeMap.end() )
11104       {
11105         aNewNode = n2n->second;
11106       }
11107       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11108       {
11109         // duplicate node
11110         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11111         copyPosition( aCurrNode, aNewNode );
11112         theNodeNodeMap[ aCurrNode ] = aNewNode;
11113         myLastCreatedNodes.push_back( aNewNode );
11114       }
11115       isDuplicate |= (aCurrNode != aNewNode);
11116       newNodes[ ind++ ] = aNewNode;
11117     }
11118     if ( !isDuplicate )
11119       continue;
11120
11121     if ( theIsDoubleElem )
11122       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11123     else
11124     {
11125       // change element nodes
11126       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11127       if ( geomType == SMDSEntity_Polyhedra )
11128       {
11129         // special treatment for polyhedron
11130         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11131         if (!aPolyhedron) {
11132           MESSAGE("Warning: bad volumic element");
11133           return false;
11134         }
11135         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11136       }
11137       else
11138         // standard entity type
11139         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11140     }
11141
11142     res = true;
11143   }
11144   return res;
11145 }
11146
11147 //================================================================================
11148 /*!
11149   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11150   \param theNodes - identifiers of nodes to be doubled
11151   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11152   nodes. If list of element identifiers is empty then nodes are doubled but
11153   they not assigned to elements
11154   \return TRUE if operation has been completed successfully, FALSE otherwise
11155 */
11156 //================================================================================
11157
11158 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11159                                     const std::list< int >& theListOfModifiedElems )
11160 {
11161   ClearLastCreated();
11162
11163   if ( theListOfNodes.size() == 0 )
11164     return false;
11165
11166   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11167   if ( !aMeshDS )
11168     return false;
11169
11170   // iterate through nodes and duplicate them
11171
11172   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11173
11174   std::list< int >::const_iterator aNodeIter;
11175   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11176   {
11177     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11178     if ( !aNode )
11179       continue;
11180
11181     // duplicate node
11182
11183     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11184     if ( aNewNode )
11185     {
11186       copyPosition( aNode, aNewNode );
11187       anOldNodeToNewNode[ aNode ] = aNewNode;
11188       myLastCreatedNodes.push_back( aNewNode );
11189     }
11190   }
11191
11192   // Change nodes of elements
11193
11194   std::vector<const SMDS_MeshNode*> aNodeArr;
11195
11196   std::list< int >::const_iterator anElemIter;
11197   for ( anElemIter =  theListOfModifiedElems.begin();
11198         anElemIter != theListOfModifiedElems.end();
11199         anElemIter++ )
11200   {
11201     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11202     if ( !anElem )
11203       continue;
11204
11205     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11206     for( size_t i = 0; i < aNodeArr.size(); ++i )
11207     {
11208       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11209         anOldNodeToNewNode.find( aNodeArr[ i ]);
11210       if ( n2n != anOldNodeToNewNode.end() )
11211         aNodeArr[ i ] = n2n->second;
11212     }
11213     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11214   }
11215
11216   return true;
11217 }
11218
11219 namespace {
11220
11221   //================================================================================
11222   /*!
11223     \brief Check if element located inside shape
11224     \return TRUE if IN or ON shape, FALSE otherwise
11225   */
11226   //================================================================================
11227
11228   template<class Classifier>
11229   bool isInside(const SMDS_MeshElement* theElem,
11230                 Classifier&             theClassifier,
11231                 const double            theTol)
11232   {
11233     gp_XYZ centerXYZ (0, 0, 0);
11234     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11235       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11236
11237     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11238     theClassifier.Perform(aPnt, theTol);
11239     TopAbs_State aState = theClassifier.State();
11240     return (aState == TopAbs_IN || aState == TopAbs_ON );
11241   }
11242
11243   //================================================================================
11244   /*!
11245    * \brief Classifier of the 3D point on the TopoDS_Face
11246    *        with interaface suitable for isInside()
11247    */
11248   //================================================================================
11249
11250   struct _FaceClassifier
11251   {
11252     Extrema_ExtPS       _extremum;
11253     BRepAdaptor_Surface _surface;
11254     TopAbs_State        _state;
11255
11256     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11257     {
11258       _extremum.Initialize( _surface,
11259                             _surface.FirstUParameter(), _surface.LastUParameter(),
11260                             _surface.FirstVParameter(), _surface.LastVParameter(),
11261                             _surface.Tolerance(), _surface.Tolerance() );
11262     }
11263     void Perform(const gp_Pnt& aPnt, double theTol)
11264     {
11265       theTol *= theTol;
11266       _state = TopAbs_OUT;
11267       _extremum.Perform(aPnt);
11268       if ( _extremum.IsDone() )
11269         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11270           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11271     }
11272     TopAbs_State State() const
11273     {
11274       return _state;
11275     }
11276   };
11277 }
11278
11279 //================================================================================
11280 /*!
11281   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11282   This method is the first step of DoubleNodeElemGroupsInRegion.
11283   \param theElems - list of groups of elements (edges or faces) to be replicated
11284   \param theNodesNot - list of groups of nodes not to replicate
11285   \param theShape - shape to detect affected elements (element which geometric center
11286          located on or inside shape). If the shape is null, detection is done on faces orientations
11287          (select elements with a gravity center on the side given by faces normals).
11288          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11289          The replicated nodes should be associated to affected elements.
11290   \return true
11291   \sa DoubleNodeElemGroupsInRegion()
11292 */
11293 //================================================================================
11294
11295 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11296                                                    const TIDSortedElemSet& theNodesNot,
11297                                                    const TopoDS_Shape&     theShape,
11298                                                    TIDSortedElemSet&       theAffectedElems)
11299 {
11300   if ( theShape.IsNull() )
11301   {
11302     findAffectedElems( theElems, theAffectedElems );
11303   }
11304   else
11305   {
11306     const double aTol = Precision::Confusion();
11307     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11308     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11309     if ( theShape.ShapeType() == TopAbs_SOLID )
11310     {
11311       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11312       bsc3d->PerformInfinitePoint(aTol);
11313     }
11314     else if (theShape.ShapeType() == TopAbs_FACE )
11315     {
11316       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11317     }
11318
11319     // iterates on indicated elements and get elements by back references from their nodes
11320     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11321     for ( ;  elemItr != theElems.end(); ++elemItr )
11322     {
11323       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11324       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11325       while ( nodeItr->more() )
11326       {
11327         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11328         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11329           continue;
11330         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11331         while ( backElemItr->more() )
11332         {
11333           const SMDS_MeshElement* curElem = backElemItr->next();
11334           if ( curElem && theElems.find(curElem) == theElems.end() &&
11335                ( bsc3d.get() ?
11336                  isInside( curElem, *bsc3d, aTol ) :
11337                  isInside( curElem, *aFaceClassifier, aTol )))
11338             theAffectedElems.insert( curElem );
11339         }
11340       }
11341     }
11342   }
11343   return true;
11344 }
11345
11346 //================================================================================
11347 /*!
11348   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11349   \param theElems - group of of elements (edges or faces) to be replicated
11350   \param theNodesNot - group of nodes not to replicate
11351   \param theShape - shape to detect affected elements (element which geometric center
11352   located on or inside shape).
11353   The replicated nodes should be associated to affected elements.
11354   \return TRUE if operation has been completed successfully, FALSE otherwise
11355 */
11356 //================================================================================
11357
11358 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11359                                             const TIDSortedElemSet& theNodesNot,
11360                                             const TopoDS_Shape&     theShape )
11361 {
11362   if ( theShape.IsNull() )
11363     return false;
11364
11365   const double aTol = Precision::Confusion();
11366   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11367   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11368   if ( theShape.ShapeType() == TopAbs_SOLID )
11369   {
11370     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11371     bsc3d->PerformInfinitePoint(aTol);
11372   }
11373   else if (theShape.ShapeType() == TopAbs_FACE )
11374   {
11375     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11376   }
11377
11378   // iterates on indicated elements and get elements by back references from their nodes
11379   TIDSortedElemSet anAffected;
11380   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11381   for ( ;  elemItr != theElems.end(); ++elemItr )
11382   {
11383     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11384     if (!anElem)
11385       continue;
11386
11387     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11388     while ( nodeItr->more() )
11389     {
11390       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11391       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11392         continue;
11393       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11394       while ( backElemItr->more() )
11395       {
11396         const SMDS_MeshElement* curElem = backElemItr->next();
11397         if ( curElem && theElems.find(curElem) == theElems.end() &&
11398              ( bsc3d ?
11399                isInside( curElem, *bsc3d, aTol ) :
11400                isInside( curElem, *aFaceClassifier, aTol )))
11401           anAffected.insert( curElem );
11402       }
11403     }
11404   }
11405   return DoubleNodes( theElems, theNodesNot, anAffected );
11406 }
11407
11408 /*!
11409  *  \brief compute an oriented angle between two planes defined by four points.
11410  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11411  *  @param p0 base of the rotation axe
11412  *  @param p1 extremity of the rotation axe
11413  *  @param g1 belongs to the first plane
11414  *  @param g2 belongs to the second plane
11415  */
11416 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11417 {
11418   gp_Vec vref(p0, p1);
11419   gp_Vec v1(p0, g1);
11420   gp_Vec v2(p0, g2);
11421   gp_Vec n1 = vref.Crossed(v1);
11422   gp_Vec n2 = vref.Crossed(v2);
11423   try {
11424     return n2.AngleWithRef(n1, vref);
11425   }
11426   catch ( Standard_Failure& ) {
11427   }
11428   return Max( v1.Magnitude(), v2.Magnitude() );
11429 }
11430
11431 /*!
11432  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11433  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11434  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11435  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11436  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11437  * 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.
11438  * 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.
11439  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11440  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11441  * \param theElems - list of groups of volumes, where a group of volume is a set of
11442  *        SMDS_MeshElements sorted by Id.
11443  * \param createJointElems - if TRUE, create the elements
11444  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11445  *        the boundary between \a theDomains and the rest mesh
11446  * \return TRUE if operation has been completed successfully, FALSE otherwise
11447  */
11448 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11449                                                      bool                                 createJointElems,
11450                                                      bool                                 onAllBoundaries)
11451 {
11452   // MESSAGE("----------------------------------------------");
11453   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11454   // MESSAGE("----------------------------------------------");
11455
11456   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11457   meshDS->BuildDownWardConnectivity(true);
11458   CHRONO(50);
11459   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11460
11461   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11462   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11463   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11464
11465   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11466   std::map<int,int> celldom; // cell vtkId --> domain
11467   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11468   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11469
11470   //MESSAGE(".. Number of domains :"<<theElems.size());
11471
11472   TIDSortedElemSet theRestDomElems;
11473   const int iRestDom  = -1;
11474   const int idom0     = onAllBoundaries ? iRestDom : 0;
11475   const int nbDomains = theElems.size();
11476
11477   // Check if the domains do not share an element
11478   for (int idom = 0; idom < nbDomains-1; idom++)
11479   {
11480     //       MESSAGE("... Check of domain #" << idom);
11481     const TIDSortedElemSet& domain = theElems[idom];
11482     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11483     for (; elemItr != domain.end(); ++elemItr)
11484     {
11485       const SMDS_MeshElement* anElem = *elemItr;
11486       int idombisdeb = idom + 1 ;
11487       // check if the element belongs to a domain further in the list
11488       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11489       {
11490         const TIDSortedElemSet& domainbis = theElems[idombis];
11491         if ( domainbis.count( anElem ))
11492         {
11493           MESSAGE(".... Domain #" << idom);
11494           MESSAGE(".... Domain #" << idombis);
11495           throw SALOME_Exception("The domains are not disjoint.");
11496           return false ;
11497         }
11498       }
11499     }
11500   }
11501
11502   for (int idom = 0; idom < nbDomains; idom++)
11503   {
11504
11505     // --- build a map (face to duplicate --> volume to modify)
11506     //     with all the faces shared by 2 domains (group of elements)
11507     //     and corresponding volume of this domain, for each shared face.
11508     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11509
11510     //MESSAGE("... Neighbors of domain #" << idom);
11511     const TIDSortedElemSet& domain = theElems[idom];
11512     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11513     for (; elemItr != domain.end(); ++elemItr)
11514     {
11515       const SMDS_MeshElement* anElem = *elemItr;
11516       if (!anElem)
11517         continue;
11518       vtkIdType vtkId = anElem->GetVtkID();
11519       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11520       int neighborsVtkIds[NBMAXNEIGHBORS];
11521       int downIds[NBMAXNEIGHBORS];
11522       unsigned char downTypes[NBMAXNEIGHBORS];
11523       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11524       for (int n = 0; n < nbNeighbors; n++)
11525       {
11526         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11527         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11528         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11529         {
11530           bool ok = false;
11531           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11532           {
11533             // MESSAGE("Domain " << idombis);
11534             const TIDSortedElemSet& domainbis = theElems[idombis];
11535             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11536           }
11537           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11538           {
11539             DownIdType face(downIds[n], downTypes[n]);
11540             if (!faceDomains[face].count(idom))
11541             {
11542               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11543               celldom[vtkId] = idom;
11544               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11545             }
11546             if ( !ok )
11547             {
11548               theRestDomElems.insert( elem );
11549               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11550               celldom[neighborsVtkIds[n]] = iRestDom;
11551             }
11552           }
11553         }
11554       }
11555     }
11556   }
11557
11558   //MESSAGE("Number of shared faces " << faceDomains.size());
11559   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11560
11561   // --- explore the shared faces domain by domain,
11562   //     explore the nodes of the face and see if they belong to a cell in the domain,
11563   //     which has only a node or an edge on the border (not a shared face)
11564
11565   for (int idomain = idom0; idomain < nbDomains; idomain++)
11566   {
11567     //MESSAGE("Domain " << idomain);
11568     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11569     itface = faceDomains.begin();
11570     for (; itface != faceDomains.end(); ++itface)
11571     {
11572       const std::map<int, int>& domvol = itface->second;
11573       if (!domvol.count(idomain))
11574         continue;
11575       DownIdType face = itface->first;
11576       //MESSAGE(" --- face " << face.cellId);
11577       std::set<int> oldNodes;
11578       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11579       std::set<int>::iterator itn = oldNodes.begin();
11580       for (; itn != oldNodes.end(); ++itn)
11581       {
11582         int oldId = *itn;
11583         //MESSAGE("     node " << oldId);
11584         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11585         for (int i=0; i<l.ncells; i++)
11586         {
11587           int vtkId = l.cells[i];
11588           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11589           if (!domain.count(anElem))
11590             continue;
11591           int vtkType = grid->GetCellType(vtkId);
11592           int downId = grid->CellIdToDownId(vtkId);
11593           if (downId < 0)
11594           {
11595             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11596             continue; // not OK at this stage of the algorithm:
11597             //no cells created after BuildDownWardConnectivity
11598           }
11599           DownIdType aCell(downId, vtkType);
11600           cellDomains[aCell][idomain] = vtkId;
11601           celldom[vtkId] = idomain;
11602           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11603         }
11604       }
11605     }
11606   }
11607
11608   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11609   //     for each shared face, get the nodes
11610   //     for each node, for each domain of the face, create a clone of the node
11611
11612   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11613   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11614   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11615
11616   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11617   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11618   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11619
11620   //MESSAGE(".. Duplication of the nodes");
11621   for (int idomain = idom0; idomain < nbDomains; idomain++)
11622   {
11623     itface = faceDomains.begin();
11624     for (; itface != faceDomains.end(); ++itface)
11625     {
11626       const std::map<int, int>& domvol = itface->second;
11627       if (!domvol.count(idomain))
11628         continue;
11629       DownIdType face = itface->first;
11630       //MESSAGE(" --- face " << face.cellId);
11631       std::set<int> oldNodes;
11632       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11633       std::set<int>::iterator itn = oldNodes.begin();
11634       for (; itn != oldNodes.end(); ++itn)
11635       {
11636         int oldId = *itn;
11637         if (nodeDomains[oldId].empty())
11638         {
11639           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11640           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11641         }
11642         std::map<int, int>::const_iterator itdom = domvol.begin();
11643         for (; itdom != domvol.end(); ++itdom)
11644         {
11645           int idom = itdom->first;
11646           //MESSAGE("         domain " << idom);
11647           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11648           {
11649             if (nodeDomains[oldId].size() >= 2) // a multiple node
11650             {
11651               vector<int> orderedDoms;
11652               //MESSAGE("multiple node " << oldId);
11653               if (mutipleNodes.count(oldId))
11654                 orderedDoms = mutipleNodes[oldId];
11655               else
11656               {
11657                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11658                 for (; it != nodeDomains[oldId].end(); ++it)
11659                   orderedDoms.push_back(it->first);
11660               }
11661               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11662               //stringstream txt;
11663               //for (int i=0; i<orderedDoms.size(); i++)
11664               //  txt << orderedDoms[i] << " ";
11665               //MESSAGE("orderedDoms " << txt.str());
11666               mutipleNodes[oldId] = orderedDoms;
11667             }
11668             double *coords = grid->GetPoint(oldId);
11669             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11670             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11671             int newId = newNode->GetVtkID();
11672             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11673             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11674           }
11675         }
11676       }
11677     }
11678   }
11679
11680   //MESSAGE(".. Creation of elements");
11681   for (int idomain = idom0; idomain < nbDomains; idomain++)
11682   {
11683     itface = faceDomains.begin();
11684     for (; itface != faceDomains.end(); ++itface)
11685     {
11686       std::map<int, int> domvol = itface->second;
11687       if (!domvol.count(idomain))
11688         continue;
11689       DownIdType face = itface->first;
11690       //MESSAGE(" --- face " << face.cellId);
11691       std::set<int> oldNodes;
11692       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11693       int nbMultipleNodes = 0;
11694       std::set<int>::iterator itn = oldNodes.begin();
11695       for (; itn != oldNodes.end(); ++itn)
11696       {
11697         int oldId = *itn;
11698         if (mutipleNodes.count(oldId))
11699           nbMultipleNodes++;
11700       }
11701       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11702       {
11703         //MESSAGE("multiple Nodes detected on a shared face");
11704         int downId = itface->first.cellId;
11705         unsigned char cellType = itface->first.cellType;
11706         // --- shared edge or shared face ?
11707         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11708         {
11709           int nodes[3];
11710           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11711           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11712             if (mutipleNodes.count(nodes[i]))
11713               if (!mutipleNodesToFace.count(nodes[i]))
11714                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11715         }
11716         else // shared face (between two volumes)
11717         {
11718           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11719           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11720           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11721           for (int ie =0; ie < nbEdges; ie++)
11722           {
11723             int nodes[3];
11724             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11725             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11726             {
11727               vector<int> vn0 = mutipleNodes[nodes[0]];
11728               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11729               vector<int> doms;
11730               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11731                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11732                   if ( vn0[i0] == vn1[i1] )
11733                     doms.push_back( vn0[ i0 ]);
11734               if ( doms.size() > 2 )
11735               {
11736                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11737                 double *coords = grid->GetPoint(nodes[0]);
11738                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11739                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11740                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11741                 gp_Pnt gref;
11742                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11743                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11744                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11745                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11746                 for ( size_t id = 0; id < doms.size(); id++ )
11747                 {
11748                   int idom = doms[id];
11749                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11750                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11751                   {
11752                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11753                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11754                     if (domain.count(elem))
11755                     {
11756                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11757                       domvol[idom] = (SMDS_MeshVolume*) svol;
11758                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11759                       double values[3] = { 0,0,0 };
11760                       vtkIdType npts = 0;
11761                       vtkIdType const *pts(nullptr);
11762                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11763                       for ( vtkIdType i = 0; i < npts; ++i )
11764                       {
11765                         double *coords = grid->GetPoint( pts[i] );
11766                         for ( int j = 0; j < 3; ++j )
11767                           values[j] += coords[j] / npts;
11768                       }
11769                       if ( id == 0 )
11770                       {
11771                         gref.SetCoord( values[0], values[1], values[2] );
11772                         angleDom[idom] = 0;
11773                       }
11774                       else
11775                       {
11776                         gp_Pnt g( values[0], values[1], values[2] );
11777                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11778                         //MESSAGE("  angle=" << angleDom[idom]);
11779                       }
11780                       break;
11781                     }
11782                   }
11783                 }
11784                 map<double, int> sortedDom; // sort domains by angle
11785                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11786                   sortedDom[ia->second] = ia->first;
11787                 vector<int> vnodes;
11788                 vector<int> vdom;
11789                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11790                 {
11791                   vdom.push_back(ib->second);
11792                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11793                 }
11794                 for (int ino = 0; ino < nbNodes; ino++)
11795                   vnodes.push_back(nodes[ino]);
11796                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11797               }
11798             }
11799           }
11800         }
11801       }
11802     }
11803   }
11804
11805   // --- iterate on shared faces (volumes to modify, face to extrude)
11806   //     get node id's of the face (id SMDS = id VTK)
11807   //     create flat element with old and new nodes if requested
11808
11809   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11810   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11811
11812   std::map<int, std::map<long,int> > nodeQuadDomains;
11813   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11814
11815   //MESSAGE(".. Creation of elements: simple junction");
11816   if ( createJointElems )
11817   {
11818     string joints2DName = "joints2D";
11819     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11820     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11821     string joints3DName = "joints3D";
11822     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11823     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11824
11825     itface = faceDomains.begin();
11826     for (; itface != faceDomains.end(); ++itface)
11827     {
11828       DownIdType face = itface->first;
11829       std::set<int> oldNodes;
11830       std::set<int>::iterator itn;
11831       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11832
11833       std::map<int, int>          domvol = itface->second;
11834       std::map<int, int>::iterator itdom = domvol.begin();
11835       int     dom1 = itdom->first;
11836       int vtkVolId = itdom->second;
11837       itdom++;
11838       int           dom2 = itdom->first;
11839       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11840                                                        nodeQuadDomains);
11841       stringstream grpname;
11842       grpname << "j_";
11843       if (dom1 < dom2)
11844         grpname << dom1 << "_" << dom2;
11845       else
11846         grpname << dom2 << "_" << dom1;
11847       string namegrp = grpname.str();
11848       if (!mapOfJunctionGroups.count(namegrp))
11849         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11850       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11851       if (sgrp)
11852         sgrp->Add(vol->GetID());
11853       if (vol->GetType() == SMDSAbs_Volume)
11854         joints3DGrp->Add(vol->GetID());
11855       else if (vol->GetType() == SMDSAbs_Face)
11856         joints2DGrp->Add(vol->GetID());
11857     }
11858   }
11859
11860   // --- create volumes on multiple domain intersection if requested
11861   //     iterate on mutipleNodesToFace
11862   //     iterate on edgesMultiDomains
11863
11864   //MESSAGE(".. Creation of elements: multiple junction");
11865   if (createJointElems)
11866   {
11867     // --- iterate on mutipleNodesToFace
11868
11869     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11870     for (; itn != mutipleNodesToFace.end(); ++itn)
11871     {
11872       int node = itn->first;
11873       vector<int> orderDom = itn->second;
11874       vector<vtkIdType> orderedNodes;
11875       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11876         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11877       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11878
11879       stringstream grpname;
11880       grpname << "m2j_";
11881       grpname << 0 << "_" << 0;
11882       string namegrp = grpname.str();
11883       if (!mapOfJunctionGroups.count(namegrp))
11884         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11885       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11886       if (sgrp)
11887         sgrp->Add(face->GetID());
11888     }
11889
11890     // --- iterate on edgesMultiDomains
11891
11892     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11893     for (; ite != edgesMultiDomains.end(); ++ite)
11894     {
11895       vector<int>    nodes = ite->first;
11896       vector<int> orderDom = ite->second;
11897       vector<vtkIdType> orderedNodes;
11898       if (nodes.size() == 2)
11899       {
11900         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11901         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11902           if ( orderDom.size() == 3 )
11903             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11904               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11905           else
11906             for (int idom = orderDom.size()-1; idom >=0; idom--)
11907               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11908         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11909
11910         string namegrp = "jointsMultiples";
11911         if (!mapOfJunctionGroups.count(namegrp))
11912           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11913         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11914         if (sgrp)
11915           sgrp->Add(vol->GetID());
11916       }
11917       else
11918       {
11919         //INFOS("Quadratic multiple joints not implemented");
11920         // TODO quadratic nodes
11921       }
11922     }
11923   }
11924
11925   // --- list the explicit faces and edges of the mesh that need to be modified,
11926   //     i.e. faces and edges built with one or more duplicated nodes.
11927   //     associate these faces or edges to their corresponding domain.
11928   //     only the first domain found is kept when a face or edge is shared
11929
11930   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11931   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11932
11933   //MESSAGE(".. Modification of elements");
11934   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11935   for (int idomain = idom0; idomain < nbDomains; idomain++)
11936   {
11937     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11938     for (; itnod != nodeDomains.end(); ++itnod)
11939     {
11940       int oldId = itnod->first;
11941       //MESSAGE("     node " << oldId);
11942       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11943       for (int i = 0; i < l.ncells; i++)
11944       {
11945         int vtkId = l.cells[i];
11946         int vtkType = grid->GetCellType(vtkId);
11947         int downId = grid->CellIdToDownId(vtkId);
11948         if (downId < 0)
11949           continue; // new cells: not to be modified
11950         DownIdType aCell(downId, vtkType);
11951         int volParents[1000];
11952         int nbvol = 0;
11953         nbvol = grid->GetParentVolumes(volParents, vtkId);
11954         if ( domainType == SMDSAbs_Volume )
11955         {
11956           nbvol = grid->GetParentVolumes(volParents, vtkId);
11957         }
11958         else // domainType == SMDSAbs_Face
11959         {
11960           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11961           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11962           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11963           for (int i=0; i< nbFaces; i++)
11964           {
11965             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11966             if (vtkFaceId >= 0)
11967               volParents[nbvol++] = vtkFaceId;
11968           }
11969         }
11970         for (int j = 0; j < nbvol; j++)
11971           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11972             if (!feDom.count(vtkId))
11973             {
11974               feDom[vtkId] = idomain;
11975               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11976               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11977               //        << " type " << vtkType << " downId " << downId);
11978             }
11979       }
11980     }
11981   }
11982
11983   // --- iterate on shared faces (volumes to modify, face to extrude)
11984   //     get node id's of the face
11985   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11986
11987   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11988   for (int m=0; m<3; m++)
11989   {
11990     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11991     itface = (*amap).begin();
11992     for (; itface != (*amap).end(); ++itface)
11993     {
11994       DownIdType face = itface->first;
11995       std::set<int> oldNodes;
11996       std::set<int>::iterator itn;
11997       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11998       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11999       std::map<int, int> localClonedNodeIds;
12000
12001       std::map<int, int> domvol = itface->second;
12002       std::map<int, int>::iterator itdom = domvol.begin();
12003       for (; itdom != domvol.end(); ++itdom)
12004       {
12005         int idom = itdom->first;
12006         int vtkVolId = itdom->second;
12007         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12008         localClonedNodeIds.clear();
12009         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12010         {
12011           int oldId = *itn;
12012           if (nodeDomains[oldId].count(idom))
12013           {
12014             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12015             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12016           }
12017         }
12018         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12019       }
12020     }
12021   }
12022
12023   // Remove empty groups (issue 0022812)
12024   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12025   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12026   {
12027     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12028       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12029   }
12030
12031   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12032   grid->DeleteLinks();
12033
12034   CHRONOSTOP(50);
12035   counters::stats();
12036   return true;
12037 }
12038
12039 /*!
12040  * \brief Double nodes on some external faces and create flat elements.
12041  * Flat elements are mainly used by some types of mechanic calculations.
12042  *
12043  * Each group of the list must be constituted of faces.
12044  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12045  * @param theElems - list of groups of faces, where a group of faces is a set of
12046  * SMDS_MeshElements sorted by Id.
12047  * @return TRUE if operation has been completed successfully, FALSE otherwise
12048  */
12049 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12050 {
12051   // MESSAGE("-------------------------------------------------");
12052   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12053   // MESSAGE("-------------------------------------------------");
12054
12055   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12056
12057   // --- For each group of faces
12058   //     duplicate the nodes, create a flat element based on the face
12059   //     replace the nodes of the faces by their clones
12060
12061   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12062   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12063   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12064
12065   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12066   {
12067     const TIDSortedElemSet&           domain = theElems[idom];
12068     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12069     for ( ; elemItr != domain.end(); ++elemItr )
12070     {
12071       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12072       if (!aFace)
12073         continue;
12074       // MESSAGE("aFace=" << aFace->GetID());
12075       bool isQuad = aFace->IsQuadratic();
12076       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12077
12078       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12079
12080       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12081       while (nodeIt->more())
12082       {
12083         const SMDS_MeshNode* node = nodeIt->next();
12084         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12085         if (isMedium)
12086           ln2.push_back(node);
12087         else
12088           ln0.push_back(node);
12089
12090         const SMDS_MeshNode* clone = 0;
12091         if (!clonedNodes.count(node))
12092         {
12093           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12094           copyPosition( node, clone );
12095           clonedNodes[node] = clone;
12096         }
12097         else
12098           clone = clonedNodes[node];
12099
12100         if (isMedium)
12101           ln3.push_back(clone);
12102         else
12103           ln1.push_back(clone);
12104
12105         const SMDS_MeshNode* inter = 0;
12106         if (isQuad && (!isMedium))
12107         {
12108           if (!intermediateNodes.count(node))
12109           {
12110             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12111             copyPosition( node, inter );
12112             intermediateNodes[node] = inter;
12113           }
12114           else
12115             inter = intermediateNodes[node];
12116           ln4.push_back(inter);
12117         }
12118       }
12119
12120       // --- extrude the face
12121
12122       vector<const SMDS_MeshNode*> ln;
12123       SMDS_MeshVolume* vol = 0;
12124       vtkIdType aType = aFace->GetVtkType();
12125       switch (aType)
12126       {
12127       case VTK_TRIANGLE:
12128         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12129         // MESSAGE("vol prism " << vol->GetID());
12130         ln.push_back(ln1[0]);
12131         ln.push_back(ln1[1]);
12132         ln.push_back(ln1[2]);
12133         break;
12134       case VTK_QUAD:
12135         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12136         // MESSAGE("vol hexa " << vol->GetID());
12137         ln.push_back(ln1[0]);
12138         ln.push_back(ln1[1]);
12139         ln.push_back(ln1[2]);
12140         ln.push_back(ln1[3]);
12141         break;
12142       case VTK_QUADRATIC_TRIANGLE:
12143         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12144                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12145         // MESSAGE("vol quad prism " << vol->GetID());
12146         ln.push_back(ln1[0]);
12147         ln.push_back(ln1[1]);
12148         ln.push_back(ln1[2]);
12149         ln.push_back(ln3[0]);
12150         ln.push_back(ln3[1]);
12151         ln.push_back(ln3[2]);
12152         break;
12153       case VTK_QUADRATIC_QUAD:
12154         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12155         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12156         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12157         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12158                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12159                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12160         // MESSAGE("vol quad hexa " << vol->GetID());
12161         ln.push_back(ln1[0]);
12162         ln.push_back(ln1[1]);
12163         ln.push_back(ln1[2]);
12164         ln.push_back(ln1[3]);
12165         ln.push_back(ln3[0]);
12166         ln.push_back(ln3[1]);
12167         ln.push_back(ln3[2]);
12168         ln.push_back(ln3[3]);
12169         break;
12170       case VTK_POLYGON:
12171         break;
12172       default:
12173         break;
12174       }
12175
12176       if (vol)
12177       {
12178         stringstream grpname;
12179         grpname << "jf_";
12180         grpname << idom;
12181         string namegrp = grpname.str();
12182         if (!mapOfJunctionGroups.count(namegrp))
12183           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12184         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12185         if (sgrp)
12186           sgrp->Add(vol->GetID());
12187       }
12188
12189       // --- modify the face
12190
12191       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12192     }
12193   }
12194   return true;
12195 }
12196
12197 /*!
12198  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12199  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12200  *  groups of faces to remove inside the object, (idem edges).
12201  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12202  */
12203 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12204                                       const TopoDS_Shape&             theShape,
12205                                       SMESH_NodeSearcher*             theNodeSearcher,
12206                                       const char*                     groupName,
12207                                       std::vector<double>&            nodesCoords,
12208                                       std::vector<std::vector<int> >& listOfListOfNodes)
12209 {
12210   // MESSAGE("--------------------------------");
12211   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12212   // MESSAGE("--------------------------------");
12213
12214   // --- zone of volumes to remove is given :
12215   //     1 either by a geom shape (one or more vertices) and a radius,
12216   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12217   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12218   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12219   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12220   //     defined by it's name.
12221
12222   SMESHDS_GroupBase* groupDS = 0;
12223   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12224   while ( groupIt->more() )
12225   {
12226     groupDS = 0;
12227     SMESH_Group * group = groupIt->next();
12228     if ( !group ) continue;
12229     groupDS = group->GetGroupDS();
12230     if ( !groupDS || groupDS->IsEmpty() ) continue;
12231     std::string grpName = group->GetName();
12232     //MESSAGE("grpName=" << grpName);
12233     if (grpName == groupName)
12234       break;
12235     else
12236       groupDS = 0;
12237   }
12238
12239   bool isNodeGroup = false;
12240   bool isNodeCoords = false;
12241   if (groupDS)
12242   {
12243     if (groupDS->GetType() != SMDSAbs_Node)
12244       return;
12245     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12246   }
12247
12248   if (nodesCoords.size() > 0)
12249     isNodeCoords = true; // a list o nodes given by their coordinates
12250   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12251
12252   // --- define groups to build
12253
12254   // --- group of SMDS volumes
12255   string grpvName = groupName;
12256   grpvName += "_vol";
12257   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12258   if (!grp)
12259   {
12260     MESSAGE("group not created " << grpvName);
12261     return;
12262   }
12263   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12264
12265   // --- group of SMDS faces on the skin
12266   string grpsName = groupName;
12267   grpsName += "_skin";
12268   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12269   if (!grps)
12270   {
12271     MESSAGE("group not created " << grpsName);
12272     return;
12273   }
12274   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12275
12276   // --- group of SMDS faces internal (several shapes)
12277   string grpiName = groupName;
12278   grpiName += "_internalFaces";
12279   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12280   if (!grpi)
12281   {
12282     MESSAGE("group not created " << grpiName);
12283     return;
12284   }
12285   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12286
12287   // --- group of SMDS faces internal (several shapes)
12288   string grpeiName = groupName;
12289   grpeiName += "_internalEdges";
12290   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12291   if (!grpei)
12292   {
12293     MESSAGE("group not created " << grpeiName);
12294     return;
12295   }
12296   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12297
12298   // --- build downward connectivity
12299
12300   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12301   meshDS->BuildDownWardConnectivity(true);
12302   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12303
12304   // --- set of volumes detected inside
12305
12306   std::set<int> setOfInsideVol;
12307   std::set<int> setOfVolToCheck;
12308
12309   std::vector<gp_Pnt> gpnts;
12310
12311   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12312   {
12313     //MESSAGE("group of nodes provided");
12314     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12315     while ( elemIt->more() )
12316     {
12317       const SMDS_MeshElement* elem = elemIt->next();
12318       if (!elem)
12319         continue;
12320       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12321       if (!node)
12322         continue;
12323       SMDS_MeshElement* vol = 0;
12324       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12325       while (volItr->more())
12326       {
12327         vol = (SMDS_MeshElement*)volItr->next();
12328         setOfInsideVol.insert(vol->GetVtkID());
12329         sgrp->Add(vol->GetID());
12330       }
12331     }
12332   }
12333   else if (isNodeCoords)
12334   {
12335     //MESSAGE("list of nodes coordinates provided");
12336     size_t i = 0;
12337     int k = 0;
12338     while ( i < nodesCoords.size()-2 )
12339     {
12340       double x = nodesCoords[i++];
12341       double y = nodesCoords[i++];
12342       double z = nodesCoords[i++];
12343       gp_Pnt p = gp_Pnt(x, y ,z);
12344       gpnts.push_back(p);
12345       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12346       k++;
12347     }
12348   }
12349   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12350   {
12351     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12352     TopTools_IndexedMapOfShape vertexMap;
12353     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12354     gp_Pnt p = gp_Pnt(0,0,0);
12355     if (vertexMap.Extent() < 1)
12356       return;
12357
12358     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12359     {
12360       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12361       p = BRep_Tool::Pnt(vertex);
12362       gpnts.push_back(p);
12363       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12364     }
12365   }
12366
12367   if (gpnts.size() > 0)
12368   {
12369     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12370     //MESSAGE("startNode->nodeId " << nodeId);
12371
12372     double radius2 = radius*radius;
12373     //MESSAGE("radius2 " << radius2);
12374
12375     // --- volumes on start node
12376
12377     setOfVolToCheck.clear();
12378     SMDS_MeshElement* startVol = 0;
12379     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12380     while (volItr->more())
12381     {
12382       startVol = (SMDS_MeshElement*)volItr->next();
12383       setOfVolToCheck.insert(startVol->GetVtkID());
12384     }
12385     if (setOfVolToCheck.empty())
12386     {
12387       MESSAGE("No volumes found");
12388       return;
12389     }
12390
12391     // --- starting with central volumes then their neighbors, check if they are inside
12392     //     or outside the domain, until no more new neighbor volume is inside.
12393     //     Fill the group of inside volumes
12394
12395     std::map<int, double> mapOfNodeDistance2;
12396     std::set<int> setOfOutsideVol;
12397     while (!setOfVolToCheck.empty())
12398     {
12399       std::set<int>::iterator it = setOfVolToCheck.begin();
12400       int vtkId = *it;
12401       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12402       bool volInside = false;
12403       vtkIdType npts = 0;
12404       vtkIdType const *pts(nullptr);
12405       grid->GetCellPoints(vtkId, npts, pts);
12406       for (int i=0; i<npts; i++)
12407       {
12408         double distance2 = 0;
12409         if (mapOfNodeDistance2.count(pts[i]))
12410         {
12411           distance2 = mapOfNodeDistance2[pts[i]];
12412           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12413         }
12414         else
12415         {
12416           double *coords = grid->GetPoint(pts[i]);
12417           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12418           distance2 = 1.E40;
12419           for ( size_t j = 0; j < gpnts.size(); j++ )
12420           {
12421             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12422             if (d2 < distance2)
12423             {
12424               distance2 = d2;
12425               if (distance2 < radius2)
12426                 break;
12427             }
12428           }
12429           mapOfNodeDistance2[pts[i]] = distance2;
12430           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12431         }
12432         if (distance2 < radius2)
12433         {
12434           volInside = true; // one or more nodes inside the domain
12435           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12436           break;
12437         }
12438       }
12439       if (volInside)
12440       {
12441         setOfInsideVol.insert(vtkId);
12442         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12443         int neighborsVtkIds[NBMAXNEIGHBORS];
12444         int downIds[NBMAXNEIGHBORS];
12445         unsigned char downTypes[NBMAXNEIGHBORS];
12446         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12447         for (int n = 0; n < nbNeighbors; n++)
12448           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12449             setOfVolToCheck.insert(neighborsVtkIds[n]);
12450       }
12451       else
12452       {
12453         setOfOutsideVol.insert(vtkId);
12454         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12455       }
12456       setOfVolToCheck.erase(vtkId);
12457     }
12458   }
12459
12460   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12461   //     If yes, add the volume to the inside set
12462
12463   bool addedInside = true;
12464   std::set<int> setOfVolToReCheck;
12465   while (addedInside)
12466   {
12467     //MESSAGE(" --------------------------- re check");
12468     addedInside = false;
12469     std::set<int>::iterator itv = setOfInsideVol.begin();
12470     for (; itv != setOfInsideVol.end(); ++itv)
12471     {
12472       int vtkId = *itv;
12473       int neighborsVtkIds[NBMAXNEIGHBORS];
12474       int downIds[NBMAXNEIGHBORS];
12475       unsigned char downTypes[NBMAXNEIGHBORS];
12476       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12477       for (int n = 0; n < nbNeighbors; n++)
12478         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12479           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12480     }
12481     setOfVolToCheck = setOfVolToReCheck;
12482     setOfVolToReCheck.clear();
12483     while  (!setOfVolToCheck.empty())
12484     {
12485       std::set<int>::iterator it = setOfVolToCheck.begin();
12486       int vtkId = *it;
12487       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12488       {
12489         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12490         int countInside = 0;
12491         int neighborsVtkIds[NBMAXNEIGHBORS];
12492         int downIds[NBMAXNEIGHBORS];
12493         unsigned char downTypes[NBMAXNEIGHBORS];
12494         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12495         for (int n = 0; n < nbNeighbors; n++)
12496           if (setOfInsideVol.count(neighborsVtkIds[n]))
12497             countInside++;
12498         //MESSAGE("countInside " << countInside);
12499         if (countInside > 1)
12500         {
12501           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12502           setOfInsideVol.insert(vtkId);
12503           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12504           addedInside = true;
12505         }
12506         else
12507           setOfVolToReCheck.insert(vtkId);
12508       }
12509       setOfVolToCheck.erase(vtkId);
12510     }
12511   }
12512
12513   // --- map of Downward faces at the boundary, inside the global volume
12514   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12515   //     fill group of SMDS faces inside the volume (when several volume shapes)
12516   //     fill group of SMDS faces on the skin of the global volume (if skin)
12517
12518   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12519   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12520   std::set<int>::iterator it = setOfInsideVol.begin();
12521   for (; it != setOfInsideVol.end(); ++it)
12522   {
12523     int vtkId = *it;
12524     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12525     int neighborsVtkIds[NBMAXNEIGHBORS];
12526     int downIds[NBMAXNEIGHBORS];
12527     unsigned char downTypes[NBMAXNEIGHBORS];
12528     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12529     for (int n = 0; n < nbNeighbors; n++)
12530     {
12531       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12532       if (neighborDim == 3)
12533       {
12534         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12535         {
12536           DownIdType face(downIds[n], downTypes[n]);
12537           boundaryFaces[face] = vtkId;
12538         }
12539         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12540         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12541         if (vtkFaceId >= 0)
12542         {
12543           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12544           // find also the smds edges on this face
12545           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12546           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12547           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12548           for (int i = 0; i < nbEdges; i++)
12549           {
12550             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12551             if (vtkEdgeId >= 0)
12552               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12553           }
12554         }
12555       }
12556       else if (neighborDim == 2) // skin of the volume
12557       {
12558         DownIdType face(downIds[n], downTypes[n]);
12559         skinFaces[face] = vtkId;
12560         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12561         if (vtkFaceId >= 0)
12562           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12563       }
12564     }
12565   }
12566
12567   // --- identify the edges constituting the wire of each subshape on the skin
12568   //     define polylines with the nodes of edges, equivalent to wires
12569   //     project polylines on subshapes, and partition, to get geom faces
12570
12571   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12572   std::set<int>                 shapeIds;
12573
12574   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12575   while (itelem->more())
12576   {
12577     const SMDS_MeshElement *elem = itelem->next();
12578     int shapeId = elem->getshapeId();
12579     int   vtkId = elem->GetVtkID();
12580     if (!shapeIdToVtkIdSet.count(shapeId))
12581     {
12582       shapeIds.insert(shapeId);
12583     }
12584     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12585   }
12586
12587   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12588   std::set<DownIdType, DownIdCompare> emptyEdges;
12589
12590   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12591   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12592   {
12593     int shapeId = itShape->first;
12594     //MESSAGE(" --- Shape ID --- "<< shapeId);
12595     shapeIdToEdges[shapeId] = emptyEdges;
12596
12597     std::vector<int> nodesEdges;
12598
12599     std::set<int>::iterator its = itShape->second.begin();
12600     for (; its != itShape->second.end(); ++its)
12601     {
12602       int vtkId = *its;
12603       //MESSAGE("     " << vtkId);
12604       int neighborsVtkIds[NBMAXNEIGHBORS];
12605       int downIds[NBMAXNEIGHBORS];
12606       unsigned char downTypes[NBMAXNEIGHBORS];
12607       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12608       for (int n = 0; n < nbNeighbors; n++)
12609       {
12610         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12611           continue;
12612         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12613         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12614         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12615         {
12616           DownIdType edge(downIds[n], downTypes[n]);
12617           if (!shapeIdToEdges[shapeId].count(edge))
12618           {
12619             shapeIdToEdges[shapeId].insert(edge);
12620             int vtkNodeId[3];
12621             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12622             nodesEdges.push_back(vtkNodeId[0]);
12623             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12624             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12625           }
12626         }
12627       }
12628     }
12629
12630     std::list<int> order;
12631     if (nodesEdges.size() > 0)
12632     {
12633       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12634       nodesEdges[0] = -1;
12635       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12636       nodesEdges[1] = -1; // do not reuse this edge
12637       bool found = true;
12638       while (found)
12639       {
12640         int nodeTofind = order.back(); // try first to push back
12641         int i = 0;
12642         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12643           if (nodesEdges[i] == nodeTofind)
12644             break;
12645         if ( i == (int) nodesEdges.size() )
12646           found = false; // no follower found on back
12647         else
12648         {
12649           if (i%2) // odd ==> use the previous one
12650             if (nodesEdges[i-1] < 0)
12651               found = false;
12652             else
12653             {
12654               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12655               nodesEdges[i-1] = -1;
12656             }
12657           else // even ==> use the next one
12658             if (nodesEdges[i+1] < 0)
12659               found = false;
12660             else
12661             {
12662               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12663               nodesEdges[i+1] = -1;
12664             }
12665         }
12666         if (found)
12667           continue;
12668         // try to push front
12669         found = true;
12670         nodeTofind = order.front(); // try to push front
12671         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12672           if ( nodesEdges[i] == nodeTofind )
12673             break;
12674         if ( i == (int)nodesEdges.size() )
12675         {
12676           found = false; // no predecessor found on front
12677           continue;
12678         }
12679         if (i%2) // odd ==> use the previous one
12680           if (nodesEdges[i-1] < 0)
12681             found = false;
12682           else
12683           {
12684             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12685             nodesEdges[i-1] = -1;
12686           }
12687         else // even ==> use the next one
12688           if (nodesEdges[i+1] < 0)
12689             found = false;
12690           else
12691           {
12692             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12693             nodesEdges[i+1] = -1;
12694           }
12695       }
12696     }
12697
12698
12699     std::vector<int> nodes;
12700     nodes.push_back(shapeId);
12701     std::list<int>::iterator itl = order.begin();
12702     for (; itl != order.end(); itl++)
12703     {
12704       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12705       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12706     }
12707     listOfListOfNodes.push_back(nodes);
12708   }
12709
12710   //     partition geom faces with blocFissure
12711   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12712   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12713
12714   return;
12715 }
12716
12717
12718 //================================================================================
12719 /*!
12720  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12721  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12722  * \return TRUE if operation has been completed successfully, FALSE otherwise
12723  */
12724 //================================================================================
12725
12726 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12727 {
12728   // iterates on volume elements and detect all free faces on them
12729   SMESHDS_Mesh* aMesh = GetMeshDS();
12730   if (!aMesh)
12731     return false;
12732
12733   ElemFeatures faceType( SMDSAbs_Face );
12734   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12735   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12736   while(vIt->more())
12737   {
12738     const SMDS_MeshVolume* volume = vIt->next();
12739     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12740     vTool.SetExternalNormal();
12741     const int iQuad = volume->IsQuadratic();
12742     faceType.SetQuad( iQuad );
12743     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12744     {
12745       if (!vTool.IsFreeFace(iface))
12746         continue;
12747       nbFree++;
12748       vector<const SMDS_MeshNode *> nodes;
12749       int nbFaceNodes = vTool.NbFaceNodes(iface);
12750       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12751       int inode = 0;
12752       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12753         nodes.push_back(faceNodes[inode]);
12754
12755       if (iQuad) // add medium nodes
12756       {
12757         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12758           nodes.push_back(faceNodes[inode]);
12759         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12760           nodes.push_back(faceNodes[8]);
12761       }
12762       // add new face based on volume nodes
12763       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12764       {
12765         nbExisted++; // face already exists
12766       }
12767       else
12768       {
12769         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12770         nbCreated++;
12771       }
12772     }
12773   }
12774   return ( nbFree == ( nbExisted + nbCreated ));
12775 }
12776
12777 namespace
12778 {
12779   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12780   {
12781     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12782       return n;
12783     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12784   }
12785 }
12786 //================================================================================
12787 /*!
12788  * \brief Creates missing boundary elements
12789  *  \param elements - elements whose boundary is to be checked
12790  *  \param dimension - defines type of boundary elements to create
12791  *  \param group - a group to store created boundary elements in
12792  *  \param targetMesh - a mesh to store created boundary elements in
12793  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12794  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12795  *                                boundary elements will be copied into the targetMesh
12796  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12797  *                                boundary elements will be added into the new group
12798  *  \param aroundElements - if true, elements will be created on boundary of given
12799  *                          elements else, on boundary of the whole mesh.
12800  * \return nb of added boundary elements
12801  */
12802 //================================================================================
12803
12804 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12805                                        Bnd_Dimension           dimension,
12806                                        SMESH_Group*            group/*=0*/,
12807                                        SMESH_Mesh*             targetMesh/*=0*/,
12808                                        bool                    toCopyElements/*=false*/,
12809                                        bool                    toCopyExistingBoundary/*=false*/,
12810                                        bool                    toAddExistingBondary/*= false*/,
12811                                        bool                    aroundElements/*= false*/)
12812 {
12813   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12814   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12815   // hope that all elements are of the same type, do not check them all
12816   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12817     throw SALOME_Exception(LOCALIZED("wrong element type"));
12818
12819   if ( !targetMesh )
12820     toCopyElements = toCopyExistingBoundary = false;
12821
12822   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12823   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12824   int nbAddedBnd = 0;
12825
12826   // editor adding present bnd elements and optionally holding elements to add to the group
12827   SMESH_MeshEditor* presentEditor;
12828   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12829   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12830
12831   SMESH_MesherHelper helper( *myMesh );
12832   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12833   SMDS_VolumeTool vTool;
12834   TIDSortedElemSet avoidSet;
12835   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12836   size_t inode;
12837
12838   typedef vector<const SMDS_MeshNode*> TConnectivity;
12839   TConnectivity tgtNodes;
12840   ElemFeatures elemKind( missType ), elemToCopy;
12841
12842   vector<const SMDS_MeshElement*> presentBndElems;
12843   vector<TConnectivity>           missingBndElems;
12844   vector<int>                     freeFacets;
12845   TConnectivity nodes, elemNodes;
12846
12847   SMDS_ElemIteratorPtr eIt;
12848   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12849   else                  eIt = SMESHUtils::elemSetIterator( elements );
12850
12851   while ( eIt->more() )
12852   {
12853     const SMDS_MeshElement* elem = eIt->next();
12854     const int              iQuad = elem->IsQuadratic();
12855     elemKind.SetQuad( iQuad );
12856
12857     // ------------------------------------------------------------------------------------
12858     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12859     // ------------------------------------------------------------------------------------
12860     presentBndElems.clear();
12861     missingBndElems.clear();
12862     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12863     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12864     {
12865       const SMDS_MeshElement* otherVol = 0;
12866       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12867       {
12868         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12869              ( !aroundElements || elements.count( otherVol )))
12870           continue;
12871         freeFacets.push_back( iface );
12872       }
12873       if ( missType == SMDSAbs_Face )
12874         vTool.SetExternalNormal();
12875       for ( size_t i = 0; i < freeFacets.size(); ++i )
12876       {
12877         int                iface = freeFacets[i];
12878         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12879         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12880         if ( missType == SMDSAbs_Edge ) // boundary edges
12881         {
12882           nodes.resize( 2+iQuad );
12883           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12884           {
12885             for ( size_t j = 0; j < nodes.size(); ++j )
12886               nodes[ j ] = nn[ i+j ];
12887             if ( const SMDS_MeshElement* edge =
12888                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12889               presentBndElems.push_back( edge );
12890             else
12891               missingBndElems.push_back( nodes );
12892           }
12893         }
12894         else // boundary face
12895         {
12896           nodes.clear();
12897           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12898             nodes.push_back( nn[inode] ); // add corner nodes
12899           if (iQuad)
12900             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12901               nodes.push_back( nn[inode] ); // add medium nodes
12902           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12903           if ( iCenter > 0 )
12904             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12905
12906           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12907                                                                SMDSAbs_Face, /*noMedium=*/false ))
12908             presentBndElems.push_back( f );
12909           else
12910             missingBndElems.push_back( nodes );
12911
12912           if ( targetMesh != myMesh )
12913           {
12914             // add 1D elements on face boundary to be added to a new mesh
12915             const SMDS_MeshElement* edge;
12916             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12917             {
12918               if ( iQuad )
12919                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12920               else
12921                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12922               if ( edge && avoidSet.insert( edge ).second )
12923                 presentBndElems.push_back( edge );
12924             }
12925           }
12926         }
12927       }
12928     }
12929     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12930     {
12931       avoidSet.clear(), avoidSet.insert( elem );
12932       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12933                         SMDS_MeshElement::iterator() );
12934       elemNodes.push_back( elemNodes[0] );
12935       nodes.resize( 2 + iQuad );
12936       const int nbLinks = elem->NbCornerNodes();
12937       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12938       {
12939         nodes[0] = elemNodes[iN];
12940         nodes[1] = elemNodes[iN+1+iQuad];
12941         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12942           continue; // not free link
12943
12944         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12945         if ( const SMDS_MeshElement* edge =
12946              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12947           presentBndElems.push_back( edge );
12948         else
12949           missingBndElems.push_back( nodes );
12950       }
12951     }
12952
12953     // ---------------------------------
12954     // 2. Add missing boundary elements
12955     // ---------------------------------
12956     if ( targetMesh != myMesh )
12957       // instead of making a map of nodes in this mesh and targetMesh,
12958       // we create nodes with same IDs.
12959       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12960       {
12961         TConnectivity& srcNodes = missingBndElems[i];
12962         tgtNodes.resize( srcNodes.size() );
12963         for ( inode = 0; inode < srcNodes.size(); ++inode )
12964           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12965         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12966                                                                        missType,
12967                                                                        /*noMedium=*/false))
12968           continue;
12969         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12970         ++nbAddedBnd;
12971       }
12972     else
12973       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12974       {
12975         TConnectivity& nodes = missingBndElems[ i ];
12976         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12977                                                                        missType,
12978                                                                        /*noMedium=*/false))
12979           continue;
12980         SMDS_MeshElement* newElem =
12981           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12982         nbAddedBnd += bool( newElem );
12983
12984         // try to set a new element to a shape
12985         if ( myMesh->HasShapeToMesh() )
12986         {
12987           bool ok = true;
12988           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12989           const size_t nbN = nodes.size() / (iQuad+1 );
12990           for ( inode = 0; inode < nbN && ok; ++inode )
12991           {
12992             pair<int, TopAbs_ShapeEnum> i_stype =
12993               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12994             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12995               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12996           }
12997           if ( ok && mediumShapes.size() > 1 )
12998           {
12999             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13000             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13001             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13002             {
13003               if (( ok = ( stype_i->first != stype_i_0.first )))
13004                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13005                                         aMesh->IndexToShape( stype_i_0.second ));
13006             }
13007           }
13008           if ( ok && mediumShapes.begin()->first == missShapeType )
13009             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13010         }
13011       }
13012
13013     // ----------------------------------
13014     // 3. Copy present boundary elements
13015     // ----------------------------------
13016     if ( toCopyExistingBoundary )
13017       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13018       {
13019         const SMDS_MeshElement* e = presentBndElems[i];
13020         tgtNodes.resize( e->NbNodes() );
13021         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13022           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13023         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13024       }
13025     else // store present elements to add them to a group
13026       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13027       {
13028         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13029       }
13030
13031   } // loop on given elements
13032
13033   // ---------------------------------------------
13034   // 4. Fill group with boundary elements
13035   // ---------------------------------------------
13036   if ( group )
13037   {
13038     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13039       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13040         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13041   }
13042   tgtEditor.myLastCreatedElems.clear();
13043   tgtEditor2.myLastCreatedElems.clear();
13044
13045   // -----------------------
13046   // 5. Copy given elements
13047   // -----------------------
13048   if ( toCopyElements && targetMesh != myMesh )
13049   {
13050     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13051     else                  eIt = SMESHUtils::elemSetIterator( elements );
13052     while (eIt->more())
13053     {
13054       const SMDS_MeshElement* elem = eIt->next();
13055       tgtNodes.resize( elem->NbNodes() );
13056       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13057         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13058       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13059
13060       tgtEditor.myLastCreatedElems.clear();
13061     }
13062   }
13063   return nbAddedBnd;
13064 }
13065
13066 //================================================================================
13067 /*!
13068  * \brief Copy node position and set \a to node on the same geometry
13069  */
13070 //================================================================================
13071
13072 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13073                                      const SMDS_MeshNode* to )
13074 {
13075   if ( !from || !to ) return;
13076
13077   SMDS_PositionPtr pos = from->GetPosition();
13078   if ( !pos || from->getshapeId() < 1 ) return;
13079
13080   switch ( pos->GetTypeOfPosition() )
13081   {
13082   case SMDS_TOP_3DSPACE: break;
13083
13084   case SMDS_TOP_FACE:
13085   {
13086     SMDS_FacePositionPtr fPos = pos;
13087     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13088                                 fPos->GetUParameter(), fPos->GetVParameter() );
13089     break;
13090   }
13091   case SMDS_TOP_EDGE:
13092   {
13093     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13094     SMDS_EdgePositionPtr ePos = pos;
13095     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13096     break;
13097   }
13098   case SMDS_TOP_VERTEX:
13099   {
13100     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13101     break;
13102   }
13103   case SMDS_TOP_UNSPEC:
13104   default:;
13105   }
13106 }