Salome HOME
Merge branch 'V9_11_BR'
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2023  CEA, EDF, 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::vector<bool>::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
5223             if (SALOME::VerbosityActivated() && !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           }
5229         }
5230         if ( freeInd.empty() )
5231           continue;
5232
5233         // create wall faces for all steps;
5234         // if such a face has been already created by sweep of edge,
5235         // assure that its orientation is OK
5236         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5237         {
5238           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5239           vTool.SetExternalNormal();
5240           const int nextShift = vTool.IsForward() ? +1 : -1;
5241           list< int >::iterator ind = freeInd.begin();
5242           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5243           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5244           {
5245             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5246             int nbn = vTool.NbFaceNodes( *ind );
5247             const SMDS_MeshElement * f = 0;
5248             if ( nbn == 3 )              ///// triangle
5249             {
5250               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5251               if ( !f ||
5252                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5253               {
5254                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5255                                                      nodes[ 1 ],
5256                                                      nodes[ 1 + nextShift ] };
5257                 if ( f )
5258                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5259                 else
5260                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5261                                                                newOrder[ 2 ] ));
5262               }
5263             }
5264             else if ( nbn == 4 )       ///// quadrangle
5265             {
5266               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5267               if ( !f ||
5268                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5269               {
5270                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5271                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5272                 if ( f )
5273                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5274                 else
5275                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5276                                                                newOrder[ 2 ], newOrder[ 3 ]));
5277               }
5278             }
5279             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5280             {
5281               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5282               if ( !f ||
5283                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5284               {
5285                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5286                                                      nodes[2],
5287                                                      nodes[2 + 2*nextShift],
5288                                                      nodes[3 - 2*nextShift],
5289                                                      nodes[3],
5290                                                      nodes[3 + 2*nextShift]};
5291                 if ( f )
5292                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5293                 else
5294                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5295                                                                newOrder[ 1 ],
5296                                                                newOrder[ 2 ],
5297                                                                newOrder[ 3 ],
5298                                                                newOrder[ 4 ],
5299                                                                newOrder[ 5 ] ));
5300               }
5301             }
5302             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5303             {
5304               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5305                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5306               if ( !f ||
5307                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5308               {
5309                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5310                                                      nodes[4 - 2*nextShift],
5311                                                      nodes[4],
5312                                                      nodes[4 + 2*nextShift],
5313                                                      nodes[1],
5314                                                      nodes[5 - 2*nextShift],
5315                                                      nodes[5],
5316                                                      nodes[5 + 2*nextShift] };
5317                 if ( f )
5318                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5319                 else
5320                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5321                                                               newOrder[ 2 ], newOrder[ 3 ],
5322                                                               newOrder[ 4 ], newOrder[ 5 ],
5323                                                               newOrder[ 6 ], newOrder[ 7 ]));
5324               }
5325             }
5326             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5327             {
5328               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5329                                       SMDSAbs_Face, /*noMedium=*/false);
5330               if ( !f ||
5331                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5332               {
5333                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5334                                                      nodes[4 - 2*nextShift],
5335                                                      nodes[4],
5336                                                      nodes[4 + 2*nextShift],
5337                                                      nodes[1],
5338                                                      nodes[5 - 2*nextShift],
5339                                                      nodes[5],
5340                                                      nodes[5 + 2*nextShift],
5341                                                      nodes[8] };
5342                 if ( f )
5343                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5344                 else
5345                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5346                                                               newOrder[ 2 ], newOrder[ 3 ],
5347                                                               newOrder[ 4 ], newOrder[ 5 ],
5348                                                               newOrder[ 6 ], newOrder[ 7 ],
5349                                                               newOrder[ 8 ]));
5350               }
5351             }
5352             else  //////// polygon
5353             {
5354               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5355               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5356               if ( !f ||
5357                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5358               {
5359                 if ( !vTool.IsForward() )
5360                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5361                 if ( f )
5362                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5363                 else
5364                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5365               }
5366             }
5367
5368             while ( srcElements.size() < myLastCreatedElems.size() )
5369               srcElements.push_back( *srcEdge );
5370
5371           }  // loop on free faces
5372
5373           // go to the next volume
5374           iVol = 0;
5375           while ( iVol++ < nbVolumesByStep ) v++;
5376
5377         } // loop on steps
5378       } // loop on volumes of one step
5379     } // sweep free links into faces
5380
5381     // Make a ceiling face with a normal external to a volume
5382
5383     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5384     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5385     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5386
5387     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5388       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5389       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5390     }
5391     if ( iF >= 0 )
5392     {
5393       lastVol.SetExternalNormal();
5394       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5395       const               int nbn = lastVol.NbFaceNodes( iF );
5396       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5397       if ( !hasFreeLinks ||
5398            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5399       {
5400         const vector<int>& interlace =
5401           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5402         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5403
5404         AddElement( nodeVec, anyFace.Init( elem ));
5405
5406         while ( srcElements.size() < myLastCreatedElems.size() )
5407           srcElements.push_back( elem );
5408       }
5409     }
5410   } // loop on swept elements
5411 }
5412
5413 //=======================================================================
5414 //function : RotationSweep
5415 //purpose  :
5416 //=======================================================================
5417
5418 SMESH_MeshEditor::PGroupIDs
5419 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5420                                 const gp_Ax1&      theAxis,
5421                                 const double       theAngle,
5422                                 const int          theNbSteps,
5423                                 const double       theTol,
5424                                 const bool         theMakeGroups,
5425                                 const bool         theMakeWalls)
5426 {
5427   ClearLastCreated();
5428
5429   setElemsFirst( theElemSets );
5430   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5431   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5432
5433   // source elements for each generated one
5434   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5435   srcElems.reserve( theElemSets[0].size() );
5436   srcNodes.reserve( theElemSets[1].size() );
5437
5438   gp_Trsf aTrsf;
5439   aTrsf.SetRotation( theAxis, theAngle );
5440   gp_Trsf aTrsf2;
5441   aTrsf2.SetRotation( theAxis, theAngle/2. );
5442
5443   gp_Lin aLine( theAxis );
5444   double aSqTol = theTol * theTol;
5445
5446   SMESHDS_Mesh* aMesh = GetMeshDS();
5447
5448   TNodeOfNodeListMap mapNewNodes;
5449   TElemOfVecOfNnlmiMap mapElemNewNodes;
5450   TTElemOfElemListMap newElemsMap;
5451
5452   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5453                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5454                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5455   // loop on theElemSets
5456   TIDSortedElemSet::iterator itElem;
5457   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5458   {
5459     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5460     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5461       const SMDS_MeshElement* elem = *itElem;
5462       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5463         continue;
5464       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5465       newNodesItVec.reserve( elem->NbNodes() );
5466
5467       // loop on elem nodes
5468       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5469       while ( itN->more() )
5470       {
5471         const SMDS_MeshNode* node = cast2Node( itN->next() );
5472
5473         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5474         double coord[3];
5475         aXYZ.Coord( coord[0], coord[1], coord[2] );
5476         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5477
5478         // check if a node has been already sweeped
5479         TNodeOfNodeListMapItr nIt =
5480           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5481         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5482         if ( listNewNodes.empty() )
5483         {
5484           // check if we are to create medium nodes between corner ones
5485           bool needMediumNodes = false;
5486           if ( isQuadraticMesh )
5487           {
5488             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5489             while (it->more() && !needMediumNodes )
5490             {
5491               const SMDS_MeshElement* invElem = it->next();
5492               if ( invElem != elem && !theElems.count( invElem )) continue;
5493               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5494               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5495                 needMediumNodes = true;
5496             }
5497           }
5498
5499           // make new nodes
5500           const SMDS_MeshNode * newNode = node;
5501           for ( int i = 0; i < theNbSteps; i++ ) {
5502             if ( !isOnAxis ) {
5503               if ( needMediumNodes )  // create a medium node
5504               {
5505                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5506                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5507                 myLastCreatedNodes.push_back(newNode);
5508                 srcNodes.push_back( node );
5509                 listNewNodes.push_back( newNode );
5510                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5511               }
5512               else {
5513                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5514               }
5515               // create a corner node
5516               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5517               myLastCreatedNodes.push_back(newNode);
5518               srcNodes.push_back( node );
5519               listNewNodes.push_back( newNode );
5520             }
5521             else {
5522               listNewNodes.push_back( newNode );
5523               // if ( needMediumNodes )
5524               //   listNewNodes.push_back( newNode );
5525             }
5526           }
5527         }
5528         newNodesItVec.push_back( nIt );
5529       }
5530       // make new elements
5531       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5532     }
5533   }
5534
5535   if ( theMakeWalls )
5536     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5537
5538   PGroupIDs newGroupIDs;
5539   if ( theMakeGroups )
5540     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5541
5542   return newGroupIDs;
5543 }
5544
5545 //=======================================================================
5546 //function : ExtrusParam
5547 //purpose  : standard construction
5548 //=======================================================================
5549
5550 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5551                                             const int                theNbSteps,
5552                                             const std::list<double>& theScales,
5553                                             const std::list<double>& theAngles,
5554                                             const gp_XYZ*            theBasePoint,
5555                                             const int                theFlags,
5556                                             const double             theTolerance):
5557   myDir( theStep ),
5558   myBaseP( Precision::Infinite(), 0, 0 ),
5559   myFlags( theFlags ),
5560   myTolerance( theTolerance ),
5561   myElemsToUse( NULL )
5562 {
5563   mySteps = new TColStd_HSequenceOfReal;
5564   const double stepSize = theStep.Magnitude();
5565   for (int i=1; i<=theNbSteps; i++ )
5566     mySteps->Append( stepSize );
5567
5568   if ( !theScales.empty() )
5569   {
5570     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5571       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5572
5573     // add medium scales
5574     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5575     myScales.reserve( theNbSteps * 2 );
5576     myScales.push_back( 0.5 * ( *s1 + 1. ));
5577     myScales.push_back( *s1 );
5578     for ( ; s2 != theScales.end(); s1 = s2++ )
5579     {
5580       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5581       myScales.push_back( *s2 );
5582     }
5583   }
5584
5585   if ( !theAngles.empty() )
5586   {
5587     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5588     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5589       linearAngleVariation( theNbSteps, angles );
5590
5591     // accumulate angles
5592     double angle = 0;
5593     int nbAngles = 0;
5594     std::list<double>::iterator a1 = angles.begin(), a2;
5595     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5596     {
5597       angle += *a1;
5598       *a1 = angle;
5599     }
5600     while ( nbAngles++ < theNbSteps )
5601       angles.push_back( angles.back() );
5602
5603     // add medium angles
5604     a2 = angles.begin(), a1 = a2++;
5605     myAngles.push_back( 0.5 * *a1 );
5606     myAngles.push_back( *a1 );
5607     for ( ; a2 != angles.end(); a1 = a2++ )
5608     {
5609       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5610       myAngles.push_back( *a2 );
5611     }
5612   }
5613
5614   if ( theBasePoint )
5615   {
5616     myBaseP = *theBasePoint;
5617   }
5618
5619   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5620       ( theTolerance > 0 ))
5621   {
5622     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5623   }
5624   else
5625   {
5626     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5627   }
5628 }
5629
5630 //=======================================================================
5631 //function : ExtrusParam
5632 //purpose  : steps are given explicitly
5633 //=======================================================================
5634
5635 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5636                                             Handle(TColStd_HSequenceOfReal) theSteps,
5637                                             const int                       theFlags,
5638                                             const double                    theTolerance):
5639   myDir( theDir ),
5640   mySteps( theSteps ),
5641   myFlags( theFlags ),
5642   myTolerance( theTolerance ),
5643   myElemsToUse( NULL )
5644 {
5645   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5646       ( theTolerance > 0 ))
5647   {
5648     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5649   }
5650   else
5651   {
5652     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5653   }
5654 }
5655
5656 //=======================================================================
5657 //function : ExtrusParam
5658 //purpose  : for extrusion by normal
5659 //=======================================================================
5660
5661 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5662                                             const int    theNbSteps,
5663                                             const int    theFlags,
5664                                             const int    theDim ):
5665   myDir( 1,0,0 ),
5666   mySteps( new TColStd_HSequenceOfReal ),
5667   myFlags( theFlags ),
5668   myTolerance( 0 ),
5669   myElemsToUse( NULL )
5670 {
5671   for (int i = 0; i < theNbSteps; i++ )
5672     mySteps->Append( theStepSize );
5673
5674   if ( theDim == 1 )
5675   {
5676     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5677   }
5678   else
5679   {
5680     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5681   }
5682 }
5683
5684 //=======================================================================
5685 //function : ExtrusParam
5686 //purpose  : for extrusion along path
5687 //=======================================================================
5688
5689 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5690                                             const gp_Pnt*                   theBasePoint,
5691                                             const std::list<double>&        theScales,
5692                                             const bool                      theMakeGroups )
5693   : myBaseP( Precision::Infinite(), 0, 0 ),
5694     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5695     myPathPoints( thePoints )
5696 {
5697   if ( theBasePoint )
5698   {
5699     myBaseP = theBasePoint->XYZ();
5700   }
5701
5702   if ( !theScales.empty() )
5703   {
5704     // add medium scales
5705     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5706     myScales.reserve( thePoints.size() * 2 );
5707     myScales.push_back( 0.5 * ( 1. + *s1 ));
5708     myScales.push_back( *s1 );
5709     for ( ; s2 != theScales.end(); s1 = s2++ )
5710     {
5711       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5712       myScales.push_back( *s2 );
5713     }
5714   }
5715
5716   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5717 }
5718
5719 //=======================================================================
5720 //function : ExtrusParam::SetElementsToUse
5721 //purpose  : stores elements to use for extrusion by normal, depending on
5722 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5723 //           define myBaseP for scaling
5724 //=======================================================================
5725
5726 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5727                                                       const TIDSortedElemSet& nodes )
5728 {
5729   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5730
5731   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5732   {
5733     myBaseP.SetCoord( 0.,0.,0. );
5734     TIDSortedElemSet newNodes;
5735
5736     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5737     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5738     {
5739       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5740       TIDSortedElemSet::const_iterator itElem = elements.begin();
5741       for ( ; itElem != elements.end(); itElem++ )
5742       {
5743         const SMDS_MeshElement* elem = *itElem;
5744         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5745         while ( itN->more() ) {
5746           const SMDS_MeshElement* node = itN->next();
5747           if ( newNodes.insert( node ).second )
5748             myBaseP += SMESH_NodeXYZ( node );
5749         }
5750       }
5751     }
5752     myBaseP /= newNodes.size();
5753   }
5754 }
5755
5756 //=======================================================================
5757 //function : ExtrusParam::beginStepIter
5758 //purpose  : prepare iteration on steps
5759 //=======================================================================
5760
5761 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5762 {
5763   myWithMediumNodes = withMediumNodes;
5764   myNextStep = 1;
5765   myCurSteps.clear();
5766 }
5767 //=======================================================================
5768 //function : ExtrusParam::moreSteps
5769 //purpose  : are there more steps?
5770 //=======================================================================
5771
5772 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5773 {
5774   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5775 }
5776 //=======================================================================
5777 //function : ExtrusParam::nextStep
5778 //purpose  : returns the next step
5779 //=======================================================================
5780
5781 double SMESH_MeshEditor::ExtrusParam::nextStep()
5782 {
5783   double res = 0;
5784   if ( !myCurSteps.empty() )
5785   {
5786     res = myCurSteps.back();
5787     myCurSteps.pop_back();
5788   }
5789   else if ( myNextStep <= mySteps->Length() )
5790   {
5791     myCurSteps.push_back( mySteps->Value( myNextStep ));
5792     ++myNextStep;
5793     if ( myWithMediumNodes )
5794     {
5795       myCurSteps.back() /= 2.;
5796       myCurSteps.push_back( myCurSteps.back() );
5797     }
5798     res = nextStep();
5799   }
5800   return res;
5801 }
5802
5803 //=======================================================================
5804 //function : ExtrusParam::makeNodesByDir
5805 //purpose  : create nodes for standard extrusion
5806 //=======================================================================
5807
5808 int SMESH_MeshEditor::ExtrusParam::
5809 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5810                 const SMDS_MeshNode*              srcNode,
5811                 std::list<const SMDS_MeshNode*> & newNodes,
5812                 const bool                        makeMediumNodes)
5813 {
5814   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5815
5816   int nbNodes = 0;
5817   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5818   {
5819     p += myDir.XYZ() * nextStep();
5820     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5821     newNodes.push_back( newNode );
5822   }
5823
5824   if ( !myScales.empty() || !myAngles.empty() )
5825   {
5826     gp_XYZ  center = myBaseP;
5827     gp_Ax1  ratationAxis( center, myDir );
5828     gp_Trsf rotation;
5829
5830     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5831     size_t i = !makeMediumNodes;
5832     for ( beginStepIter( makeMediumNodes );
5833           moreSteps();
5834           ++nIt, i += 1 + !makeMediumNodes )
5835     {
5836       center += myDir.XYZ() * nextStep();
5837
5838       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5839       bool moved = false;
5840       if ( i < myScales.size() )
5841       {
5842         xyz = ( myScales[i] * ( xyz - center )) + center;
5843         moved = true;
5844       }
5845       if ( !myAngles.empty() )
5846       {
5847         rotation.SetRotation( ratationAxis, myAngles[i] );
5848         rotation.Transforms( xyz );
5849         moved = true;
5850       }
5851       if ( moved )
5852         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5853       else
5854         break;
5855     }
5856   }
5857   return nbNodes;
5858 }
5859
5860 //=======================================================================
5861 //function : ExtrusParam::makeNodesByDirAndSew
5862 //purpose  : create nodes for standard extrusion with sewing
5863 //=======================================================================
5864
5865 int SMESH_MeshEditor::ExtrusParam::
5866 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5867                       const SMDS_MeshNode*              srcNode,
5868                       std::list<const SMDS_MeshNode*> & newNodes,
5869                       const bool                        makeMediumNodes)
5870 {
5871   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5872
5873   int nbNodes = 0;
5874   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5875   {
5876     P1 += myDir.XYZ() * nextStep();
5877
5878     // try to search in sequence of existing nodes
5879     // if myNodes.size()>0 we 'nave to use given sequence
5880     // else - use all nodes of mesh
5881     const SMDS_MeshNode * node = 0;
5882     if ( myNodes.Length() > 0 )
5883     {
5884       for ( int i = 1; i <= myNodes.Length(); i++ )
5885       {
5886         SMESH_NodeXYZ P2 = myNodes.Value(i);
5887         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5888         {
5889           node = myNodes.Value(i);
5890           break;
5891         }
5892       }
5893     }
5894     else
5895     {
5896       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5897       while(itn->more())
5898       {
5899         SMESH_NodeXYZ P2 = itn->next();
5900         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5901         {
5902           node = P2._node;
5903           break;
5904         }
5905       }
5906     }
5907
5908     if ( !node )
5909       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5910
5911     newNodes.push_back( node );
5912
5913   } // loop on steps
5914
5915   return nbNodes;
5916 }
5917
5918 //=======================================================================
5919 //function : ExtrusParam::makeNodesByNormal2D
5920 //purpose  : create nodes for extrusion using normals of faces
5921 //=======================================================================
5922
5923 int SMESH_MeshEditor::ExtrusParam::
5924 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5925                      const SMDS_MeshNode*              srcNode,
5926                      std::list<const SMDS_MeshNode*> & newNodes,
5927                      const bool                        makeMediumNodes)
5928 {
5929   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5930
5931   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5932
5933   // get normals to faces sharing srcNode
5934   vector< gp_XYZ > norms, baryCenters;
5935   gp_XYZ norm, avgNorm( 0,0,0 );
5936   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5937   while ( faceIt->more() )
5938   {
5939     const SMDS_MeshElement* face = faceIt->next();
5940     if ( myElemsToUse && !myElemsToUse->count( face ))
5941       continue;
5942     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5943     {
5944       norms.push_back( norm );
5945       avgNorm += norm;
5946       if ( !alongAvgNorm )
5947       {
5948         gp_XYZ bc(0,0,0);
5949         int nbN = 0;
5950         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5951           bc += SMESH_NodeXYZ( nIt->next() );
5952         baryCenters.push_back( bc / nbN );
5953       }
5954     }
5955   }
5956
5957   if ( norms.empty() ) return 0;
5958
5959   double normSize = avgNorm.Modulus();
5960   if ( normSize < std::numeric_limits<double>::min() )
5961     return 0;
5962
5963   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5964   {
5965     myDir = avgNorm;
5966     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5967   }
5968
5969   avgNorm /= normSize;
5970
5971   int nbNodes = 0;
5972   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5973   {
5974     gp_XYZ pNew = p;
5975     double stepSize = nextStep();
5976
5977     if ( norms.size() > 1 )
5978     {
5979       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5980       {
5981         // translate plane of a face
5982         baryCenters[ iF ] += norms[ iF ] * stepSize;
5983
5984         // find point of intersection of the face plane located at baryCenters[ iF ]
5985         // and avgNorm located at pNew
5986         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5987         double dot  = ( norms[ iF ] * avgNorm );
5988         if ( dot < std::numeric_limits<double>::min() )
5989           dot = stepSize * 1e-3;
5990         double step = -( norms[ iF ] * pNew + d ) / dot;
5991         pNew += step * avgNorm;
5992       }
5993     }
5994     else
5995     {
5996       pNew += stepSize * avgNorm;
5997     }
5998     p = pNew;
5999
6000     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6001     newNodes.push_back( newNode );
6002   }
6003   return nbNodes;
6004 }
6005
6006 //=======================================================================
6007 //function : ExtrusParam::makeNodesByNormal1D
6008 //purpose  : create nodes for extrusion using normals of edges
6009 //=======================================================================
6010
6011 int SMESH_MeshEditor::ExtrusParam::
6012 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
6013                      const SMDS_MeshNode*              /*srcNode*/,
6014                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
6015                      const bool                        /*makeMediumNodes*/)
6016 {
6017   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6018   return 0;
6019 }
6020
6021 //=======================================================================
6022 //function : ExtrusParam::makeNodesAlongTrack
6023 //purpose  : create nodes for extrusion along path
6024 //=======================================================================
6025
6026 int SMESH_MeshEditor::ExtrusParam::
6027 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
6028                      const SMDS_MeshNode*              srcNode,
6029                      std::list<const SMDS_MeshNode*> & newNodes,
6030                      const bool                        makeMediumNodes)
6031 {
6032   const Standard_Real aTolAng=1.e-4;
6033
6034   gp_Pnt aV0x = myBaseP;
6035   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6036
6037   const PathPoint& aPP0 = myPathPoints[0];
6038   gp_Pnt aP0x = aPP0.myPnt;
6039   gp_Dir aDT0x= aPP0.myTgt;
6040
6041   std::vector< gp_Pnt > centers;
6042   centers.reserve( NbSteps() * 2 );
6043
6044   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6045
6046   for ( size_t j = 1; j < myPathPoints.size(); ++j )
6047   {
6048     const PathPoint&  aPP  = myPathPoints[j];
6049     const gp_Pnt&     aP1x = aPP.myPnt;
6050     const gp_Dir&    aDT1x = aPP.myTgt;
6051
6052     // Translation
6053     gp_Vec aV01x( aP0x, aP1x );
6054     aTrsf.SetTranslation( aV01x );
6055     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6056     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6057
6058     // rotation 1 [ T1,T0 ]
6059     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6060     if ( fabs( aAngleT1T0 ) > aTolAng )
6061     {
6062       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6063       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6064
6065       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6066     }
6067
6068     // rotation 2
6069     if ( aPP.myAngle != 0. )
6070     {
6071       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6072       aPN1 = aPN1.Transformed( aTrsfRot );
6073     }
6074
6075     // make new node
6076     if ( makeMediumNodes )
6077     {
6078       // create additional node
6079       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6080       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6081       newNodes.push_back( newNode );
6082
6083     }
6084     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6085     newNodes.push_back( newNode );
6086
6087     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6088     centers.push_back( aV1x );
6089
6090     aPN0 = aPN1;
6091     aP0x = aP1x;
6092     aV0x = aV1x;
6093     aDT0x = aDT1x;
6094   }
6095
6096   // scale
6097   if ( !myScales.empty() )
6098   {
6099     gp_Trsf aTrsfScale;
6100     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6101     for ( size_t i = !makeMediumNodes;
6102           i < myScales.size() && node != newNodes.end();
6103           i += ( 1 + !makeMediumNodes ), ++node )
6104     {
6105       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6106       gp_Pnt aN = SMESH_NodeXYZ( *node );
6107       gp_Pnt aP = aN.Transformed( aTrsfScale );
6108       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6109     }
6110   }
6111
6112   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6113 }
6114
6115 //=======================================================================
6116 //function : ExtrusionSweep
6117 //purpose  :
6118 //=======================================================================
6119
6120 SMESH_MeshEditor::PGroupIDs
6121 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
6122                                   const gp_Vec&        theStep,
6123                                   const int            theNbSteps,
6124                                   TTElemOfElemListMap& newElemsMap,
6125                                   const int            theFlags,
6126                                   const double         theTolerance)
6127 {
6128   std::list<double> dummy;
6129   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6130                        theFlags, theTolerance );
6131   return ExtrusionSweep( theElems, aParams, newElemsMap );
6132 }
6133
6134 namespace
6135 {
6136
6137 //=======================================================================
6138 //function : getOriFactor
6139 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
6140 //           edge curve orientation
6141 //=======================================================================
6142
6143   double getOriFactor( const TopoDS_Edge&   edge,
6144                        const SMDS_MeshNode* n1,
6145                        const SMDS_MeshNode* n2,
6146                        SMESH_MesherHelper&  helper)
6147   {
6148     double u1 = helper.GetNodeU( edge, n1, n2 );
6149     double u2 = helper.GetNodeU( edge, n2, n1 );
6150     return u1 < u2 ? 1. : -1.;
6151   }
6152 }
6153
6154 //=======================================================================
6155 //function : ExtrusionSweep
6156 //purpose  :
6157 //=======================================================================
6158
6159 SMESH_MeshEditor::PGroupIDs
6160 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
6161                                   ExtrusParam&         theParams,
6162                                   TTElemOfElemListMap& newElemsMap)
6163 {
6164   ClearLastCreated();
6165
6166   setElemsFirst( theElemSets );
6167   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6168   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6169
6170   // source elements for each generated one
6171   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6172   srcElems.reserve( theElemSets[0].size() );
6173   srcNodes.reserve( theElemSets[1].size() );
6174
6175   const int nbSteps = theParams.NbSteps();
6176   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6177
6178   TNodeOfNodeListMap   mapNewNodes;
6179   TElemOfVecOfNnlmiMap mapElemNewNodes;
6180
6181   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6182                                      myMesh->NbFaces(ORDER_QUADRATIC) +
6183                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
6184   // loop on theElems
6185   TIDSortedElemSet::iterator itElem;
6186   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6187   {
6188     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6189     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6190     {
6191       // check element type
6192       const SMDS_MeshElement* elem = *itElem;
6193       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
6194         continue;
6195
6196       const size_t nbNodes = elem->NbNodes();
6197       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6198       newNodesItVec.reserve( nbNodes );
6199
6200       // loop on elem nodes
6201       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6202       while ( itN->more() )
6203       {
6204         // check if a node has been already sweeped
6205         const SMDS_MeshNode* node = itN->next();
6206         TNodeOfNodeListMap::iterator nIt =
6207           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6208         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6209         if ( listNewNodes.empty() )
6210         {
6211           // make new nodes
6212
6213           // check if we are to create medium nodes between corner ones
6214           bool needMediumNodes = false;
6215           if ( isQuadraticMesh )
6216           {
6217             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6218             while (it->more() && !needMediumNodes )
6219             {
6220               const SMDS_MeshElement* invElem = it->next();
6221               if ( invElem != elem && !theElems.count( invElem )) continue;
6222               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6223               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6224                 needMediumNodes = true;
6225             }
6226           }
6227           // create nodes for all steps
6228           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6229           {
6230             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6231             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6232             {
6233               myLastCreatedNodes.push_back( *newNodesIt );
6234               srcNodes.push_back( node );
6235             }
6236           }
6237           else
6238           {
6239             if ( theParams.ToMakeBoundary() )
6240             {
6241               GetMeshDS()->Modified();
6242               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6243             }
6244             break; // newNodesItVec will be shorter than nbNodes
6245           }
6246         }
6247         newNodesItVec.push_back( nIt );
6248       }
6249       // make new elements
6250       if ( newNodesItVec.size() == nbNodes )
6251         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6252     }
6253   }
6254
6255   if ( theParams.ToMakeBoundary() ) {
6256     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6257   }
6258   PGroupIDs newGroupIDs;
6259   if ( theParams.ToMakeGroups() )
6260     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6261
6262   return newGroupIDs;
6263 }
6264
6265 //=======================================================================
6266 //function : ExtrusionAlongTrack
6267 //purpose  :
6268 //=======================================================================
6269 SMESH_MeshEditor::Extrusion_Error
6270 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6271                                        SMESH_Mesh*          theTrackMesh,
6272                                        SMDS_ElemIteratorPtr theTrackIterator,
6273                                        const SMDS_MeshNode* theN1,
6274                                        std::list<double>&   theAngles,
6275                                        const bool           theAngleVariation,
6276                                        std::list<double>&   theScales,
6277                                        const bool           theScaleVariation,
6278                                        const gp_Pnt*        theRefPoint,
6279                                        const bool           theMakeGroups)
6280 {
6281   ClearLastCreated();
6282
6283   // 1. Check data
6284   if ( theElements[0].empty() && theElements[1].empty() )
6285     return EXTR_NO_ELEMENTS;
6286
6287   ASSERT( theTrackMesh );
6288   if ( ! theTrackIterator || !theTrackIterator->more() )
6289     return EXTR_NO_ELEMENTS;
6290
6291   // 2. Get ordered nodes
6292   SMESH_MeshAlgos::TElemGroupVector branchEdges;
6293   SMESH_MeshAlgos::TNodeGroupVector branchNods;
6294   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6295   if ( branchEdges.empty() )
6296     return EXTR_PATH_NOT_EDGE;
6297
6298   if ( branchEdges.size() > 1 )
6299     return EXTR_BAD_PATH_SHAPE;
6300
6301   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
6302   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6303   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6304     return EXTR_BAD_STARTING_NODE;
6305
6306   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6307   {
6308     // add medium nodes to pathNodes
6309     std::vector< const SMDS_MeshNode* >    pathNodes2;
6310     std::vector< const SMDS_MeshElement* > pathEdges2;
6311     pathNodes2.reserve( pathNodes.size() * 2 );
6312     pathEdges2.reserve( pathEdges.size() * 2 );
6313     for ( size_t i = 0; i < pathEdges.size(); ++i )
6314     {
6315       pathNodes2.push_back( pathNodes[i] );
6316       pathEdges2.push_back( pathEdges[i] );
6317       if ( pathEdges[i]->IsQuadratic() )
6318       {
6319         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6320         pathEdges2.push_back( pathEdges[i] );
6321       }
6322     }
6323     pathNodes2.push_back( pathNodes.back() );
6324     pathEdges.swap( pathEdges2 );
6325     pathNodes.swap( pathNodes2 );
6326   }
6327
6328   // 3. Get path data at pathNodes
6329
6330   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6331
6332   if ( theAngleVariation )
6333     linearAngleVariation( points.size()-1, theAngles );
6334   if ( theScaleVariation )
6335     linearScaleVariation( points.size()-1, theScales );
6336
6337   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6338   std::list<double>::iterator angle = theAngles.begin();
6339
6340   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6341
6342   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6343   std::map< int, double >::iterator id2factor;
6344   SMESH_MesherHelper pathHelper( *theTrackMesh );
6345   gp_Pnt p; gp_Vec tangent;
6346   const double tol2 = gp::Resolution() * gp::Resolution();
6347
6348   for ( size_t i = 0; i < pathNodes.size(); ++i )
6349   {
6350     ExtrusParam::PathPoint & point = points[ i ];
6351
6352     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6353
6354     if ( angle != theAngles.end() )
6355       point.myAngle = *angle++;
6356
6357     tangent.SetCoord( 0,0,0 );
6358     const int          shapeID = pathNodes[ i ]->GetShapeID();
6359     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6360     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6361     switch ( shapeType )
6362     {
6363     case TopAbs_EDGE:
6364     {
6365       TopoDS_Edge edge = TopoDS::Edge( shape );
6366       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6367       if ( id2factor->second == 0 )
6368       {
6369         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6370         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6371       }
6372       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6373       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6374       curve->D1( u, p, tangent );
6375       tangent *= id2factor->second;
6376       break;
6377     }
6378     case TopAbs_VERTEX:
6379     {
6380       int nbEdges = 0;
6381       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6382       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6383       {
6384         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6385         for ( int di = -1; di <= 0; ++di )
6386         {
6387           size_t j = i + di;
6388           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6389           {
6390             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6391             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6392             if ( id2factor->second == 0 )
6393             {
6394               if ( j < i )
6395                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6396               else
6397                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6398             }
6399             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6400             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6401             gp_Vec du;
6402             curve->D1( u, p, du );
6403             double size2 = du.SquareMagnitude();
6404             if ( du.SquareMagnitude() > tol2 )
6405             {
6406               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6407               nbEdges++;
6408             }
6409             break;
6410           }
6411         }
6412       }
6413       if ( nbEdges > 0 )
6414         break;
6415     }
6416     // fall through
6417     default:
6418     {
6419       for ( int di = -1; di <= 1; di += 2 )
6420       {
6421         size_t j = i + di;
6422         if ( j < pathNodes.size() )
6423         {
6424           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6425           double size2 = dir.SquareMagnitude();
6426           if ( size2 > tol2 )
6427             tangent += dir.Divided( Sqrt( size2 )) * di;
6428         }
6429       }
6430     }
6431     } // switch ( shapeType )
6432
6433     if ( tangent.SquareMagnitude() < tol2 )
6434       return EXTR_CANT_GET_TANGENT;
6435
6436     point.myTgt = tangent;
6437
6438   } // loop on pathNodes
6439
6440
6441   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6442   TTElemOfElemListMap newElemsMap;
6443
6444   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6445
6446   return EXTR_OK;
6447 }
6448
6449 //=======================================================================
6450 //function : linearAngleVariation
6451 //purpose  : spread values over nbSteps
6452 //=======================================================================
6453
6454 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6455                                             list<double>& Angles)
6456 {
6457   int nbAngles = Angles.size();
6458   if( nbSteps > nbAngles && nbAngles > 0 )
6459   {
6460     vector<double> theAngles(nbAngles);
6461     theAngles.assign( Angles.begin(), Angles.end() );
6462
6463     list<double> res;
6464     double rAn2St = double( nbAngles ) / double( nbSteps );
6465     double angPrev = 0, angle;
6466     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6467     {
6468       double angCur = rAn2St * ( iSt+1 );
6469       double angCurFloor  = floor( angCur );
6470       double angPrevFloor = floor( angPrev );
6471       if ( angPrevFloor == angCurFloor )
6472         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6473       else {
6474         int iP = int( angPrevFloor );
6475         double angPrevCeil = ceil(angPrev);
6476         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6477
6478         int iC = int( angCurFloor );
6479         if ( iC < nbAngles )
6480           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6481
6482         iP = int( angPrevCeil );
6483         while ( iC-- > iP )
6484           angle += theAngles[ iC ];
6485       }
6486       res.push_back(angle);
6487       angPrev = angCur;
6488     }
6489     Angles.swap( res );
6490   }
6491 }
6492
6493 //=======================================================================
6494 //function : linearScaleVariation
6495 //purpose  : spread values over nbSteps 
6496 //=======================================================================
6497
6498 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6499                                             std::list<double>& theScales)
6500 {
6501   int nbScales = theScales.size();
6502   std::vector<double> myScales;
6503   myScales.reserve( theNbSteps );
6504   std::list<double>::const_iterator scale = theScales.begin();
6505   double prevScale = 1.0;
6506   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6507   {
6508     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6509     int    stDelta = Max( 1, iStep - myScales.size());
6510     double scDelta = ( *scale - prevScale ) / stDelta;
6511     for ( int iStep = 0; iStep < stDelta; ++iStep )
6512     {
6513       myScales.push_back( prevScale + scDelta );
6514       prevScale = myScales.back();
6515     }
6516     prevScale = *scale;
6517   }
6518   theScales.assign( myScales.begin(), myScales.end() );
6519 }
6520
6521 //================================================================================
6522 /*!
6523  * \brief Move or copy theElements applying theTrsf to their nodes
6524  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6525  *  \param theTrsf - transformation to apply
6526  *  \param theCopy - if true, create translated copies of theElems
6527  *  \param theMakeGroups - if true and theCopy, create translated groups
6528  *  \param theTargetMesh - mesh to copy translated elements into
6529  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6530  */
6531 //================================================================================
6532
6533 SMESH_MeshEditor::PGroupIDs
6534 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6535                              const gp_Trsf&     theTrsf,
6536                              const bool         theCopy,
6537                              const bool         theMakeGroups,
6538                              SMESH_Mesh*        theTargetMesh)
6539 {
6540   ClearLastCreated();
6541   myLastCreatedElems.reserve( theElems.size() );
6542
6543   bool needReverse = false;
6544   string groupPostfix;
6545   switch ( theTrsf.Form() ) {
6546   case gp_PntMirror:
6547     needReverse = true;
6548     groupPostfix = "mirrored";
6549     break;
6550   case gp_Ax1Mirror:
6551     groupPostfix = "mirrored";
6552     break;
6553   case gp_Ax2Mirror:
6554     needReverse = true;
6555     groupPostfix = "mirrored";
6556     break;
6557   case gp_Rotation:
6558     groupPostfix = "rotated";
6559     break;
6560   case gp_Translation:
6561     groupPostfix = "translated";
6562     break;
6563   case gp_Scale:
6564     groupPostfix = "scaled";
6565     break;
6566   case gp_CompoundTrsf: // different scale by axis
6567     groupPostfix = "scaled";
6568     break;
6569   default:
6570     needReverse = false;
6571     groupPostfix = "transformed";
6572   }
6573
6574   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6575   SMESHDS_Mesh* aMesh    = GetMeshDS();
6576
6577   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6578   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6579   SMESH_MeshEditor::ElemFeatures elemType;
6580
6581   // map old node to new one
6582   TNodeNodeMap nodeMap;
6583
6584   // elements sharing moved nodes; those of them which have all
6585   // nodes mirrored but are not in theElems are to be reversed
6586   TIDSortedElemSet inverseElemSet;
6587
6588   // source elements for each generated one
6589   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6590
6591   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6592   TIDSortedElemSet orphanNode;
6593
6594   if ( theElems.empty() ) // transform the whole mesh
6595   {
6596     // add all elements
6597     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6598     while ( eIt->more() ) theElems.insert( eIt->next() );
6599     // add orphan nodes
6600     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6601     while ( nIt->more() )
6602     {
6603       const SMDS_MeshNode* node = nIt->next();
6604       if ( node->NbInverseElements() == 0)
6605         orphanNode.insert( node );
6606     }
6607   }
6608
6609   // loop on elements to transform nodes : first orphan nodes then elems
6610   TIDSortedElemSet::iterator itElem;
6611   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6612   for (int i=0; i<2; i++)
6613     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6614     {
6615       const SMDS_MeshElement* elem = *itElem;
6616       if ( !elem )
6617         continue;
6618
6619       // loop on elem nodes
6620       double coord[3];
6621       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6622       while ( itN->more() )
6623       {
6624         const SMDS_MeshNode* node = cast2Node( itN->next() );
6625         // check if a node has been already transformed
6626         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6627           nodeMap.insert( make_pair ( node, node ));
6628         if ( !n2n_isnew.second )
6629           continue;
6630
6631         node->GetXYZ( coord );
6632         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6633         if ( theTargetMesh ) {
6634           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6635           n2n_isnew.first->second = newNode;
6636           myLastCreatedNodes.push_back(newNode);
6637           srcNodes.push_back( node );
6638         }
6639         else if ( theCopy ) {
6640           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6641           n2n_isnew.first->second = newNode;
6642           myLastCreatedNodes.push_back(newNode);
6643           srcNodes.push_back( node );
6644         }
6645         else {
6646           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6647           // node position on shape becomes invalid
6648           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6649             ( SMDS_SpacePosition::originSpacePosition() );
6650         }
6651
6652         // keep inverse elements
6653         if ( !theCopy && !theTargetMesh && needReverse ) {
6654           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6655           while ( invElemIt->more() ) {
6656             const SMDS_MeshElement* iel = invElemIt->next();
6657             inverseElemSet.insert( iel );
6658           }
6659         }
6660       }
6661     } // loop on elems in { &orphanNode, &theElems };
6662
6663   // either create new elements or reverse mirrored ones
6664   if ( !theCopy && !needReverse && !theTargetMesh )
6665     return PGroupIDs();
6666
6667   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6668
6669   // Replicate or reverse elements
6670
6671   std::vector<int> iForw;
6672   vector<const SMDS_MeshNode*> nodes;
6673   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6674   {
6675     const SMDS_MeshElement* elem = *itElem;
6676     if ( !elem ) continue;
6677
6678     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6679     size_t               nbNodes  = elem->NbNodes();
6680     if ( geomType == SMDSGeom_NONE ) continue; // node
6681
6682     nodes.resize( nbNodes );
6683
6684     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6685     {
6686       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6687       if ( !aPolyedre )
6688         continue;
6689       nodes.clear();
6690       bool allTransformed = true;
6691       int nbFaces = aPolyedre->NbFaces();
6692       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6693       {
6694         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6695         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6696         {
6697           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6698           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6699           if ( nodeMapIt == nodeMap.end() )
6700             allTransformed = false; // not all nodes transformed
6701           else
6702             nodes.push_back((*nodeMapIt).second);
6703         }
6704         if ( needReverse && allTransformed )
6705           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6706       }
6707       if ( !allTransformed )
6708         continue; // not all nodes transformed
6709     }
6710     else // ----------------------- the rest element types
6711     {
6712       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6713       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6714       const vector<int>&    i = needReverse ? iRev : iForw;
6715
6716       // find transformed nodes
6717       size_t iNode = 0;
6718       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6719       while ( itN->more() ) {
6720         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6721         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6722         if ( nodeMapIt == nodeMap.end() )
6723           break; // not all nodes transformed
6724         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6725       }
6726       if ( iNode != nbNodes )
6727         continue; // not all nodes transformed
6728     }
6729
6730     if ( editor ) {
6731       // copy in this or a new mesh
6732       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6733         srcElems.push_back( elem );
6734     }
6735     else {
6736       // reverse element as it was reversed by transformation
6737       if ( nbNodes > 2 )
6738         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6739     }
6740
6741   } // loop on elements
6742
6743   if ( editor && editor != this )
6744     myLastCreatedElems.swap( editor->myLastCreatedElems );
6745
6746   PGroupIDs newGroupIDs;
6747
6748   if ( ( theMakeGroups && theCopy ) ||
6749        ( theMakeGroups && theTargetMesh ) )
6750     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6751
6752   return newGroupIDs;
6753 }
6754
6755 //================================================================================
6756 /*!
6757  * \brief Make an offset mesh from a source 2D mesh
6758  *  \param [in] theElements - source faces
6759  *  \param [in] theValue - offset value
6760  *  \param [out] theTgtMesh - a mesh to add offset elements to
6761  *  \param [in] theMakeGroups - to generate groups
6762  *  \return PGroupIDs - IDs of created groups. NULL means failure
6763  */
6764 //================================================================================
6765
6766 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6767                                                       const double       theValue,
6768                                                       SMESH_Mesh*        theTgtMesh,
6769                                                       const bool         theMakeGroups,
6770                                                       const bool         theCopyElements,
6771                                                       const bool         theFixSelfIntersection)
6772 {
6773   SMESHDS_Mesh*    meshDS = GetMeshDS();
6774   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6775   SMESH_MeshEditor tgtEditor( theTgtMesh );
6776
6777   SMDS_ElemIteratorPtr eIt;
6778   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6779   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6780
6781   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6782   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6783   std::unique_ptr< SMDS_Mesh > offsetMesh
6784     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6785                                    theFixSelfIntersection,
6786                                    new2OldFaces, new2OldNodes ));
6787   if ( offsetMesh->NbElements() == 0 )
6788     return PGroupIDs(); // MakeOffset() failed
6789
6790
6791   if ( theTgtMesh == myMesh && !theCopyElements )
6792   {
6793     // clear the source elements
6794     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6795     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6796     while ( eIt->more() )
6797       meshDS->RemoveFreeElement( eIt->next(), 0 );
6798   }
6799
6800   // offsetMesh->Modified();
6801   // offsetMesh->CompactMesh(); // make IDs start from 1
6802
6803   // source elements for each generated one
6804   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6805   srcElems.reserve( new2OldFaces.size() );
6806   srcNodes.reserve( new2OldNodes.size() );
6807
6808   ClearLastCreated();
6809   myLastCreatedElems.reserve( new2OldFaces.size() );
6810   myLastCreatedNodes.reserve( new2OldNodes.size() );
6811
6812   // copy offsetMesh to theTgtMesh
6813
6814   smIdType idShift = meshDS->MaxNodeID();
6815   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6816     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6817     {
6818
6819       if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6820       {
6821         const SMDS_MeshNode* n2 =
6822           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6823         myLastCreatedNodes.push_back( n2 );
6824         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6825       }
6826     }
6827
6828   ElemFeatures elemType;
6829   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6830     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6831     {
6832       elemType.Init( f );
6833       elemType.myNodes.clear();
6834       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6835       {
6836         const SMDS_MeshNode* n2 = nIt->next();
6837         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6838       }
6839       tgtEditor.AddElement( elemType.myNodes, elemType );
6840       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6841     }
6842
6843   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6844
6845   PGroupIDs newGroupIDs;
6846   if ( theMakeGroups )
6847     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6848   else
6849     newGroupIDs.reset( new std::list< int > );
6850
6851   return newGroupIDs;
6852 }
6853
6854 //=======================================================================
6855 /*!
6856  * \brief Create groups of elements made during transformation
6857  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6858  *  \param elemGens - elements making corresponding myLastCreatedElems
6859  *  \param postfix - to push_back to names of new groups
6860  *  \param targetMesh - mesh to create groups in
6861  *  \param topPresent - is there are "top" elements that are created by sweeping
6862  */
6863 //=======================================================================
6864
6865 SMESH_MeshEditor::PGroupIDs
6866 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6867                                  const SMESH_SequenceOfElemPtr& elemGens,
6868                                  const std::string&             postfix,
6869                                  SMESH_Mesh*                    targetMesh,
6870                                  const bool                     topPresent)
6871 {
6872   PGroupIDs newGroupIDs( new list<int> );
6873   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6874
6875   // Sort existing groups by types and collect their names
6876
6877   // containers to store an old group and generated new ones;
6878   // 1st new group is for result elems of different type than a source one;
6879   // 2nd new group is for same type result elems ("top" group at extrusion)
6880   using boost::tuple;
6881   using boost::make_tuple;
6882   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6883   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6884   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6885   // group names
6886   set< string > groupNames;
6887
6888   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6889   if ( !groupIt->more() ) return newGroupIDs;
6890
6891   int newGroupID = mesh->GetGroupIds().back()+1;
6892   while ( groupIt->more() )
6893   {
6894     SMESH_Group * group = groupIt->next();
6895     if ( !group ) continue;
6896     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6897     if ( !groupDS || groupDS->IsEmpty() ) continue;
6898     groupNames.insert    ( group->GetName() );
6899     groupDS->SetStoreName( group->GetName() );
6900     const SMDSAbs_ElementType type = groupDS->GetType();
6901     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6902     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6903     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6904     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6905   }
6906
6907   // Loop on nodes and elements to add them in new groups
6908
6909   vector< const SMDS_MeshElement* > resultElems;
6910   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6911   {
6912     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6913     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6914     if ( gens.size() != elems.size() )
6915       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6916
6917     // loop on created elements
6918     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6919     {
6920       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6921       if ( !sourceElem ) {
6922         MESSAGE("generateGroups(): NULL source element");
6923         continue;
6924       }
6925       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6926       if ( groupsOldNew.empty() ) { // no groups of this type at all
6927         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6928           ++iElem; // skip all elements made by sourceElem
6929         continue;
6930       }
6931       // collect all elements made by the iElem-th sourceElem
6932       resultElems.clear();
6933       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6934         if ( resElem != sourceElem )
6935           resultElems.push_back( resElem );
6936       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6937         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6938           if ( resElem != sourceElem )
6939             resultElems.push_back( resElem );
6940
6941       const SMDS_MeshElement* topElem = 0;
6942       if ( isNodes ) // there must be a top element
6943       {
6944         topElem = resultElems.back();
6945         resultElems.pop_back();
6946       }
6947       else
6948       {
6949         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6950         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6951           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6952           {
6953             topElem = *resElemIt;
6954             *resElemIt = 0; // erase *resElemIt
6955             break;
6956           }
6957       }
6958       // add resultElems to groups originted from ones the sourceElem belongs to
6959       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6960       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6961       {
6962         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6963         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6964         {
6965           // fill in a new group
6966           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6967           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6968           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6969             if ( *resElemIt )
6970               newGroup.Add( *resElemIt );
6971
6972           // fill a "top" group
6973           if ( topElem )
6974           {
6975             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6976             newTopGroup.Add( topElem );
6977           }
6978         }
6979       }
6980     } // loop on created elements
6981   }// loop on nodes and elements
6982
6983   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6984
6985   list<int> topGrouIds;
6986   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6987   {
6988     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6989     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6990                                       orderedOldNewGroups[i]->get<2>() };
6991     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6992     {
6993       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6994       if ( newGroupDS->IsEmpty() )
6995       {
6996         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6997       }
6998       else
6999       {
7000         // set group type
7001         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7002
7003         // make a name
7004         const bool isTop = ( topPresent &&
7005                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7006                              is2nd );
7007
7008         string name = oldGroupDS->GetStoreName();
7009         { // remove trailing whitespaces (issue 22599)
7010           size_t size = name.size();
7011           while ( size > 1 && isspace( name[ size-1 ]))
7012             --size;
7013           if ( size != name.size() )
7014           {
7015             name.resize( size );
7016             oldGroupDS->SetStoreName( name.c_str() );
7017           }
7018         }
7019         if ( !targetMesh ) {
7020           string suffix = ( isTop ? "top": postfix.c_str() );
7021           name += "_";
7022           name += suffix;
7023           int nb = 1;
7024           while ( !groupNames.insert( name ).second ) // name exists
7025             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7026         }
7027         else if ( isTop ) {
7028           name += "_top";
7029         }
7030         newGroupDS->SetStoreName( name.c_str() );
7031
7032         // make a SMESH_Groups
7033         mesh->AddGroup( newGroupDS );
7034         if ( isTop )
7035           topGrouIds.push_back( newGroupDS->GetID() );
7036         else
7037           newGroupIDs->push_back( newGroupDS->GetID() );
7038       }
7039     }
7040   }
7041   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7042
7043   return newGroupIDs;
7044 }
7045
7046 //================================================================================
7047 /*!
7048  *  * \brief Return list of group of nodes close to each other within theTolerance
7049  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7050  *  *        an Octree algorithm
7051  *  \param [in,out] theNodes - the nodes to treat
7052  *  \param [in]     theTolerance - the tolerance
7053  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7054  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7055  *         corner and medium nodes in separate groups
7056  */
7057 //================================================================================
7058
7059 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7060                                             const double         theTolerance,
7061                                             TListOfListOfNodes & theGroupsOfNodes,
7062                                             bool                 theSeparateCornersAndMedium)
7063 {
7064   ClearLastCreated();
7065
7066   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7067        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7068        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7069     theSeparateCornersAndMedium = false;
7070
7071   TIDSortedNodeSet& corners = theNodes;
7072   TIDSortedNodeSet  medium;
7073
7074   if ( theNodes.empty() ) // get all nodes in the mesh
7075   {
7076     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7077     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7078     if ( theSeparateCornersAndMedium )
7079       while ( nIt->more() )
7080       {
7081         const SMDS_MeshNode* n = nIt->next();
7082         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7083         nodeSet->insert( nodeSet->end(), n );
7084       }
7085     else
7086       while ( nIt->more() )
7087         theNodes.insert( theNodes.end(), nIt->next() );
7088   }
7089   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7090   {
7091     TIDSortedNodeSet::iterator nIt = corners.begin();
7092     while ( nIt != corners.end() )
7093       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7094       {
7095         medium.insert( medium.end(), *nIt );
7096         corners.erase( nIt++ );
7097       }
7098       else
7099       {
7100         ++nIt;
7101       }
7102   }
7103
7104   if ( !corners.empty() )
7105     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7106   if ( !medium.empty() )
7107     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7108 }
7109
7110 //=======================================================================
7111 //function : SimplifyFace
7112 //purpose  : split a chain of nodes into several closed chains
7113 //=======================================================================
7114
7115 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7116                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7117                                     vector<int>&                         quantities) const
7118 {
7119   int nbNodes = faceNodes.size();
7120   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7121     --nbNodes;
7122   if ( nbNodes < 3 )
7123     return 0;
7124   size_t prevNbQuant = quantities.size();
7125
7126   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7127   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7128   map< const SMDS_MeshNode*, int >::iterator nInd;
7129
7130   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7131   simpleNodes.push_back( faceNodes[0] );
7132   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7133   {
7134     if ( faceNodes[ iCur ] != simpleNodes.back() )
7135     {
7136       int index = simpleNodes.size();
7137       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7138       int prevIndex = nInd->second;
7139       if ( prevIndex < index )
7140       {
7141         // a sub-loop found
7142         int loopLen = index - prevIndex;
7143         if ( loopLen > 2 )
7144         {
7145           // store the sub-loop
7146           quantities.push_back( loopLen );
7147           for ( int i = prevIndex; i < index; i++ )
7148             poly_nodes.push_back( simpleNodes[ i ]);
7149         }
7150         simpleNodes.resize( prevIndex+1 );
7151       }
7152       else
7153       {
7154         simpleNodes.push_back( faceNodes[ iCur ]);
7155       }
7156     }
7157   }
7158
7159   if ( simpleNodes.size() > 2 )
7160   {
7161     quantities.push_back( simpleNodes.size() );
7162     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7163   }
7164
7165   return quantities.size() - prevNbQuant;
7166 }
7167
7168 //=======================================================================
7169 //function : MergeNodes
7170 //purpose  : In each group, the cdr of nodes are substituted by the first one
7171 //           in all elements.
7172 //=======================================================================
7173
7174 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7175                                    const bool           theAvoidMakingHoles)
7176 {
7177   ClearLastCreated();
7178
7179   SMESHDS_Mesh* mesh = GetMeshDS();
7180
7181   TNodeNodeMap nodeNodeMap; // node to replace - new node
7182   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7183   list< smIdType > rmElemIds, rmNodeIds;
7184   vector< ElemFeatures > newElemDefs;
7185
7186   // Fill nodeNodeMap and elems
7187
7188   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7189   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7190   {
7191     list<const SMDS_MeshNode*>& nodes = *grIt;
7192     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7193     const SMDS_MeshNode* nToKeep = *nIt;
7194     for ( ++nIt; nIt != nodes.end(); nIt++ )
7195     {
7196       const SMDS_MeshNode* nToRemove = *nIt;
7197       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7198       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7199       while ( invElemIt->more() ) {
7200         const SMDS_MeshElement* elem = invElemIt->next();
7201         elems.insert(elem);
7202       }
7203     }
7204   }
7205
7206   // Apply recursive replacements (BUG 0020185)
7207   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7208   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7209   {
7210     const SMDS_MeshNode* nToKeep = nnIt->second;
7211     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7212     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7213     {
7214       nToKeep = nnIt_i->second;
7215       nnIt->second = nToKeep;
7216       nnIt_i = nodeNodeMap.find( nToKeep );
7217     }
7218   }
7219
7220   if ( theAvoidMakingHoles )
7221   {
7222     // find elements whose topology changes
7223
7224     vector<const SMDS_MeshElement*> pbElems;
7225     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7226     for ( ; eIt != elems.end(); ++eIt )
7227     {
7228       const SMDS_MeshElement* elem = *eIt;
7229       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7230       while ( itN->more() )
7231       {
7232         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7233         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7234         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7235         {
7236           // several nodes of elem stick
7237           pbElems.push_back( elem );
7238           break;
7239         }
7240       }
7241     }
7242     // exclude from merge nodes causing spoiling element
7243     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7244     {
7245       bool nodesExcluded = false;
7246       for ( size_t i = 0; i < pbElems.size(); ++i )
7247       {
7248         size_t prevNbMergeNodes = nodeNodeMap.size();
7249         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7250              prevNbMergeNodes < nodeNodeMap.size() )
7251           nodesExcluded = true;
7252       }
7253       if ( !nodesExcluded )
7254         break;
7255     }
7256   }
7257
7258   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7259   {
7260     const SMDS_MeshNode* nToRemove = nnIt->first;
7261     const SMDS_MeshNode* nToKeep   = nnIt->second;
7262     if ( nToRemove != nToKeep )
7263     {
7264       rmNodeIds.push_back( nToRemove->GetID() );
7265       AddToSameGroups( nToKeep, nToRemove, mesh );
7266       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7267       // w/o creating node in place of merged ones.
7268       SMDS_PositionPtr pos = nToRemove->GetPosition();
7269       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7270         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7271           sm->SetIsAlwaysComputed( true );
7272     }
7273   }
7274
7275   // Change element nodes or remove an element
7276
7277   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7278   for ( ; eIt != elems.end(); eIt++ )
7279   {
7280     const SMDS_MeshElement* elem = *eIt;
7281     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7282     bool                 marked = elem->isMarked();
7283
7284     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7285     if ( !keepElem )
7286       rmElemIds.push_back( elem->GetID() );
7287
7288     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7289     {
7290       bool elemChanged = false;
7291       if ( i == 0 )
7292       {
7293         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7294           elemChanged = mesh->ChangePolyhedronNodes( elem,
7295                                                      newElemDefs[i].myNodes,
7296                                                      newElemDefs[i].myPolyhedQuantities );
7297         else
7298           elemChanged = mesh->ChangeElementNodes( elem,
7299                                                   & newElemDefs[i].myNodes[0],
7300                                                   newElemDefs[i].myNodes.size() );
7301       }
7302       if ( i > 0 || !elemChanged )
7303       {
7304         if ( i == 0 )
7305         {
7306           newElemDefs[i].SetID( elem->GetID() );
7307           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7308           if ( !keepElem ) rmElemIds.pop_back();
7309         }
7310         else
7311         {
7312           newElemDefs[i].SetID( -1 );
7313         }
7314         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7315         if ( sm && newElem )
7316           sm->AddElement( newElem );
7317         if ( elem != newElem )
7318           ReplaceElemInGroups( elem, newElem, mesh );
7319         if ( marked && newElem )
7320           newElem->setIsMarked( true );
7321       }
7322     }
7323   }
7324
7325   // Remove bad elements, then equal nodes (order important)
7326   Remove( rmElemIds, /*isNodes=*/false );
7327   Remove( rmNodeIds, /*isNodes=*/true );
7328
7329   return;
7330 }
7331
7332 //=======================================================================
7333 //function : applyMerge
7334 //purpose  : Compute new connectivity of an element after merging nodes
7335 //  \param [in] elems - the element
7336 //  \param [out] newElemDefs - definition(s) of result element(s)
7337 //  \param [inout] nodeNodeMap - nodes to merge
7338 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7339 //              after merging (but not degenerated), removes nodes causing
7340 //              the invalidity from \a nodeNodeMap.
7341 //  \return bool - true if the element should be removed
7342 //=======================================================================
7343
7344 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7345                                    vector< ElemFeatures >& newElemDefs,
7346                                    TNodeNodeMap&           nodeNodeMap,
7347                                    const bool              avoidMakingHoles )
7348 {
7349   bool toRemove = false; // to remove elem
7350   int nbResElems = 1;    // nb new elements
7351
7352   newElemDefs.resize(nbResElems);
7353   newElemDefs[0].Init( elem );
7354   newElemDefs[0].myNodes.clear();
7355
7356   set<const SMDS_MeshNode*> nodeSet;
7357   vector< const SMDS_MeshNode*>   curNodes;
7358   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7359   vector<int> iRepl;
7360
7361   const        int  nbNodes = elem->NbNodes();
7362   SMDSAbs_EntityType entity = elem->GetEntityType();
7363
7364   curNodes.resize( nbNodes );
7365   uniqueNodes.resize( nbNodes );
7366   iRepl.resize( nbNodes );
7367   int iUnique = 0, iCur = 0, nbRepl = 0;
7368
7369   // Get new seq of nodes
7370
7371   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7372   while ( itN->more() )
7373   {
7374     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7375
7376     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7377     if ( nnIt != nodeNodeMap.end() ) {
7378       n = (*nnIt).second;
7379     }
7380     curNodes[ iCur ] = n;
7381     bool isUnique = nodeSet.insert( n ).second;
7382     if ( isUnique )
7383       uniqueNodes[ iUnique++ ] = n;
7384     else
7385       iRepl[ nbRepl++ ] = iCur;
7386     iCur++;
7387   }
7388
7389   // Analyse element topology after replacement
7390
7391   int nbUniqueNodes = nodeSet.size();
7392   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7393   {
7394     toRemove = true;
7395     nbResElems = 0;
7396
7397     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7398     {
7399       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7400       int nbCorners = nbNodes / 2;
7401       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7402       {
7403         int iNext = ( iCur + 1 ) % nbCorners;
7404         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7405         {
7406           int iMedium = iCur + nbCorners;
7407           vector< const SMDS_MeshNode* >::iterator i =
7408             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7409                        uniqueNodes.end(),
7410                        curNodes[ iMedium ]);
7411           if ( i != uniqueNodes.end() )
7412           {
7413             --nbUniqueNodes;
7414             for ( ; i+1 != uniqueNodes.end(); ++i )
7415               *i = *(i+1);
7416           }
7417         }
7418       }
7419     }
7420
7421     switch ( entity )
7422     {
7423     case SMDSEntity_Polygon:
7424     case SMDSEntity_Quad_Polygon: // Polygon
7425     {
7426       ElemFeatures* elemType = & newElemDefs[0];
7427       const bool isQuad = elemType->myIsQuad;
7428       if ( isQuad )
7429         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7430           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7431
7432       // a polygon can divide into several elements
7433       vector<const SMDS_MeshNode *> polygons_nodes;
7434       vector<int> quantities;
7435       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7436       newElemDefs.resize( nbResElems );
7437       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7438       {
7439         ElemFeatures* elemType = & newElemDefs[iface];
7440         if ( iface ) elemType->Init( elem );
7441
7442         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7443         int nbNewNodes = quantities[iface];
7444         face_nodes.assign( polygons_nodes.begin() + inode,
7445                            polygons_nodes.begin() + inode + nbNewNodes );
7446         inode += nbNewNodes;
7447         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7448         {
7449           bool isValid = ( nbNewNodes % 2 == 0 );
7450           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7451             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7452           elemType->SetQuad( isValid );
7453           if ( isValid ) // put medium nodes after corners
7454             SMDS_MeshCell::applyInterlaceRev
7455               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7456                                                     nbNewNodes ), face_nodes );
7457         }
7458         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7459       }
7460       nbUniqueNodes = newElemDefs[0].myNodes.size();
7461       break;
7462     } // Polygon
7463
7464     case SMDSEntity_Polyhedra: // Polyhedral volume
7465     {
7466       if ( nbUniqueNodes >= 4 )
7467       {
7468         // each face has to be analyzed in order to check volume validity
7469         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7470         {
7471           toRemove = false;
7472           int nbFaces = aPolyedre->NbFaces();
7473
7474           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7475           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7476           vector<const SMDS_MeshNode *>  faceNodes;
7477           poly_nodes.clear();
7478           quantities.clear();
7479
7480           for (int iface = 1; iface <= nbFaces; iface++)
7481           {
7482             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7483             faceNodes.resize( nbFaceNodes );
7484             for (int inode = 1; inode <= nbFaceNodes; inode++)
7485             {
7486               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7487               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7488               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7489                 faceNode = (*nnIt).second;
7490               faceNodes[inode - 1] = faceNode;
7491             }
7492             SimplifyFace(faceNodes, poly_nodes, quantities);
7493           }
7494
7495           if ( quantities.size() > 3 )
7496           {
7497             // TODO: remove coincident faces
7498             nbResElems = 1;
7499             nbUniqueNodes = newElemDefs[0].myNodes.size();
7500           }
7501         }
7502       }
7503     }
7504     break;
7505
7506     // Regular elements
7507     // TODO not all the possible cases are solved. Find something more generic?
7508     case SMDSEntity_Edge: //////// EDGE
7509     case SMDSEntity_Triangle: //// TRIANGLE
7510     case SMDSEntity_Quad_Triangle:
7511     case SMDSEntity_Tetra:
7512     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7513     {
7514       break;
7515     }
7516     case SMDSEntity_Quad_Edge:
7517     {
7518       break;
7519     }
7520     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7521     {
7522       if ( nbUniqueNodes < 3 )
7523         toRemove = true;
7524       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7525         toRemove = true; // opposite nodes stick
7526       else
7527         toRemove = false;
7528       break;
7529     }
7530     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7531     {
7532       //   1    5    2
7533       //    +---+---+
7534       //    |       |
7535       //   4+       +6
7536       //    |       |
7537       //    +---+---+
7538       //   0    7    3
7539       if ( nbUniqueNodes == 6 &&
7540            iRepl[0] < 4       &&
7541            ( nbRepl == 1 || iRepl[1] >= 4 ))
7542       {
7543         toRemove = false;
7544       }
7545       break;
7546     }
7547     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7548     {
7549       //   1    5    2
7550       //    +---+---+
7551       //    |       |
7552       //   4+  8+   +6
7553       //    |       |
7554       //    +---+---+
7555       //   0    7    3
7556       if ( nbUniqueNodes == 7 &&
7557            iRepl[0] < 4       &&
7558            ( nbRepl == 1 || iRepl[1] != 8 ))
7559       {
7560         toRemove = false;
7561       }
7562       break;
7563     }
7564     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7565     {
7566       if ( nbUniqueNodes == 4 ) {
7567         // ---------------------------------> tetrahedron
7568         if ( curNodes[3] == curNodes[4] &&
7569              curNodes[3] == curNodes[5] ) {
7570           // top nodes stick
7571           toRemove = false;
7572         }
7573         else if ( curNodes[0] == curNodes[1] &&
7574                   curNodes[0] == curNodes[2] ) {
7575           // bottom nodes stick: set a top before
7576           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7577           uniqueNodes[ 0 ] = curNodes [ 5 ];
7578           uniqueNodes[ 1 ] = curNodes [ 4 ];
7579           uniqueNodes[ 2 ] = curNodes [ 3 ];
7580           toRemove = false;
7581         }
7582         else if (( curNodes[0] == curNodes[3] ) +
7583                  ( curNodes[1] == curNodes[4] ) +
7584                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7585           // a lateral face turns into a line
7586           toRemove = false;
7587         }
7588       }
7589       else if ( nbUniqueNodes == 5 ) {
7590         // PENTAHEDRON --------------------> pyramid
7591         if ( curNodes[0] == curNodes[3] )
7592         {
7593           uniqueNodes[ 0 ] = curNodes[ 1 ];
7594           uniqueNodes[ 1 ] = curNodes[ 4 ];
7595           uniqueNodes[ 2 ] = curNodes[ 5 ];
7596           uniqueNodes[ 3 ] = curNodes[ 2 ];
7597           uniqueNodes[ 4 ] = curNodes[ 0 ];
7598           toRemove = false;
7599         }
7600         if ( curNodes[1] == curNodes[4] )
7601         {
7602           uniqueNodes[ 0 ] = curNodes[ 0 ];
7603           uniqueNodes[ 1 ] = curNodes[ 2 ];
7604           uniqueNodes[ 2 ] = curNodes[ 5 ];
7605           uniqueNodes[ 3 ] = curNodes[ 3 ];
7606           uniqueNodes[ 4 ] = curNodes[ 1 ];
7607           toRemove = false;
7608         }
7609         if ( curNodes[2] == curNodes[5] )
7610         {
7611           uniqueNodes[ 0 ] = curNodes[ 0 ];
7612           uniqueNodes[ 1 ] = curNodes[ 3 ];
7613           uniqueNodes[ 2 ] = curNodes[ 4 ];
7614           uniqueNodes[ 3 ] = curNodes[ 1 ];
7615           uniqueNodes[ 4 ] = curNodes[ 2 ];
7616           toRemove = false;
7617         }
7618       }
7619       break;
7620     }
7621     case SMDSEntity_Hexa:
7622     {
7623       //////////////////////////////////// HEXAHEDRON
7624       SMDS_VolumeTool hexa (elem);
7625       hexa.SetExternalNormal();
7626       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7627         //////////////////////// HEX ---> tetrahedron
7628         for ( int iFace = 0; iFace < 6; iFace++ ) {
7629           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7630           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7631               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7632               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7633             // one face turns into a point ...
7634             int  pickInd = ind[ 0 ];
7635             int iOppFace = hexa.GetOppFaceIndex( iFace );
7636             ind = hexa.GetFaceNodesIndices( iOppFace );
7637             int nbStick = 0;
7638             uniqueNodes.clear();
7639             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7640               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7641                 nbStick++;
7642               else
7643                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7644             }
7645             if ( nbStick == 1 ) {
7646               // ... and the opposite one - into a triangle.
7647               // set a top node
7648               uniqueNodes.push_back( curNodes[ pickInd ]);
7649               toRemove = false;
7650             }
7651             break;
7652           }
7653         }
7654       }
7655       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7656         //////////////////////// HEX ---> prism
7657         int nbTria = 0, iTria[3];
7658         const int *ind; // indices of face nodes
7659         // look for triangular faces
7660         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7661           ind = hexa.GetFaceNodesIndices( iFace );
7662           TIDSortedNodeSet faceNodes;
7663           for ( iCur = 0; iCur < 4; iCur++ )
7664             faceNodes.insert( curNodes[ind[iCur]] );
7665           if ( faceNodes.size() == 3 )
7666             iTria[ nbTria++ ] = iFace;
7667         }
7668         // check if triangles are opposite
7669         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7670         {
7671           // set nodes of the bottom triangle
7672           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7673           vector<int> indB;
7674           for ( iCur = 0; iCur < 4; iCur++ )
7675             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7676               indB.push_back( ind[iCur] );
7677           if ( !hexa.IsForward() )
7678             std::swap( indB[0], indB[2] );
7679           for ( iCur = 0; iCur < 3; iCur++ )
7680             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7681           // set nodes of the top triangle
7682           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7683           for ( iCur = 0; iCur < 3; ++iCur )
7684             for ( int j = 0; j < 4; ++j )
7685               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7686               {
7687                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7688                 break;
7689               }
7690           toRemove = false;
7691           break;
7692         }
7693       }
7694       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7695         //////////////////// HEXAHEDRON ---> pyramid
7696         for ( int iFace = 0; iFace < 6; iFace++ ) {
7697           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7698           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7699               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7700               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7701             // one face turns into a point ...
7702             int iOppFace = hexa.GetOppFaceIndex( iFace );
7703             ind = hexa.GetFaceNodesIndices( iOppFace );
7704             uniqueNodes.clear();
7705             for ( iCur = 0; iCur < 4; iCur++ ) {
7706               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7707                 break;
7708               else
7709                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7710             }
7711             if ( uniqueNodes.size() == 4 ) {
7712               // ... and the opposite one is a quadrangle
7713               // set a top node
7714               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7715               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7716               toRemove = false;
7717             }
7718             break;
7719           }
7720         }
7721       }
7722
7723       if ( toRemove && nbUniqueNodes > 4 ) {
7724         ////////////////// HEXAHEDRON ---> polyhedron
7725         hexa.SetExternalNormal();
7726         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7727         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7728         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7729         quantities.reserve( 6 );     quantities.clear();
7730         for ( int iFace = 0; iFace < 6; iFace++ )
7731         {
7732           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7733           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7734                curNodes[ind[1]] == curNodes[ind[3]] )
7735           {
7736             quantities.clear();
7737             break; // opposite nodes stick
7738           }
7739           nodeSet.clear();
7740           for ( iCur = 0; iCur < 4; iCur++ )
7741           {
7742             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7743               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7744           }
7745           if ( nodeSet.size() < 3 )
7746             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7747           else
7748             quantities.push_back( nodeSet.size() );
7749         }
7750         if ( quantities.size() >= 4 )
7751         {
7752           nbResElems = 1;
7753           nbUniqueNodes = poly_nodes.size();
7754           newElemDefs[0].SetPoly(true);
7755         }
7756       }
7757       break;
7758     } // case HEXAHEDRON
7759
7760     default:
7761       toRemove = true;
7762
7763     } // switch ( entity )
7764
7765     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7766     {
7767       // erase from nodeNodeMap nodes whose merge spoils elem
7768       vector< const SMDS_MeshNode* > noMergeNodes;
7769       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7770       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7771         nodeNodeMap.erase( noMergeNodes[i] );
7772     }
7773     
7774   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7775
7776   uniqueNodes.resize( nbUniqueNodes );
7777
7778   if ( !toRemove && nbResElems == 0 )
7779     nbResElems = 1;
7780
7781   newElemDefs.resize( nbResElems );
7782
7783   return !toRemove;
7784 }
7785
7786
7787 // ========================================================
7788 // class   : ComparableElement
7789 // purpose : allow comparing elements basing on their nodes
7790 // ========================================================
7791
7792 class ComparableElement : public boost::container::flat_set< smIdType >
7793 {
7794   typedef boost::container::flat_set< smIdType >  int_set;
7795
7796   const SMDS_MeshElement* myElem;
7797   smIdType                mySumID;
7798   mutable int             myGroupID;
7799
7800 public:
7801
7802   ComparableElement( const SMDS_MeshElement* theElem ):
7803     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7804   {
7805     this->reserve( theElem->NbNodes() );
7806     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7807     {
7808       smIdType id = nodeIt->next()->GetID();
7809       mySumID += id;
7810       this->insert( id );
7811     }
7812   }
7813
7814   const SMDS_MeshElement* GetElem() const { return myElem; }
7815
7816   int& GroupID() const { return myGroupID; }
7817   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7818
7819   ComparableElement( const ComparableElement& theSource ) // move copy
7820     : int_set()
7821   {
7822     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7823     (int_set&) (*this ) = std::move( src );
7824     myElem    = src.myElem;
7825     mySumID   = src.mySumID;
7826     myGroupID = src.myGroupID;
7827   }
7828
7829   static int HashCode(const ComparableElement& se, int limit )
7830   {
7831     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7832   }
7833   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7834   {
7835     return ( se1 == se2 );
7836   }
7837
7838 };
7839
7840 //=======================================================================
7841 //function : FindEqualElements
7842 //purpose  : Return list of group of elements built on the same nodes.
7843 //           Search among theElements or in the whole mesh if theElements is empty
7844 //=======================================================================
7845
7846 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7847                                           TListOfListOfElementsID & theGroupsOfElementsID )
7848 {
7849   ClearLastCreated();
7850
7851   SMDS_ElemIteratorPtr elemIt;
7852   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7853   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7854
7855   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7856   typedef std::list<smIdType>                                     TGroupOfElems;
7857   TMapOfElements               mapOfElements;
7858   std::vector< TGroupOfElems > arrayOfGroups;
7859   TGroupOfElems                groupOfElems;
7860
7861   while ( elemIt->more() )
7862   {
7863     const SMDS_MeshElement* curElem = elemIt->next();
7864     if ( curElem->IsNull() )
7865       continue;
7866     ComparableElement      compElem = curElem;
7867     // check uniqueness
7868     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7869     if ( elemInSet.GetElem() != curElem ) // coincident elem
7870     {
7871       int& iG = elemInSet.GroupID();
7872       if ( iG < 0 )
7873       {
7874         iG = arrayOfGroups.size();
7875         arrayOfGroups.push_back( groupOfElems );
7876         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7877       }
7878       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7879     }
7880   }
7881
7882   groupOfElems.clear();
7883   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7884   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7885   {
7886     if ( groupIt->size() > 1 ) {
7887       //groupOfElems.sort(); -- theElements are sorted already
7888       theGroupsOfElementsID.emplace_back( *groupIt );
7889     }
7890   }
7891 }
7892
7893 //=======================================================================
7894 //function : MergeElements
7895 //purpose  : In each given group, substitute all elements by the first one.
7896 //=======================================================================
7897
7898 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7899 {
7900   ClearLastCreated();
7901
7902   typedef list<smIdType> TListOfIDs;
7903   TListOfIDs rmElemIds; // IDs of elems to remove
7904
7905   SMESHDS_Mesh* aMesh = GetMeshDS();
7906
7907   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7908   while ( groupsIt != theGroupsOfElementsID.end() ) {
7909     TListOfIDs& aGroupOfElemID = *groupsIt;
7910     aGroupOfElemID.sort();
7911     int elemIDToKeep = aGroupOfElemID.front();
7912     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7913     aGroupOfElemID.pop_front();
7914     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7915     while ( idIt != aGroupOfElemID.end() ) {
7916       int elemIDToRemove = *idIt;
7917       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7918       // add the kept element in groups of removed one (PAL15188)
7919       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7920       rmElemIds.push_back( elemIDToRemove );
7921       ++idIt;
7922     }
7923     ++groupsIt;
7924   }
7925
7926   Remove( rmElemIds, false );
7927 }
7928
7929 //=======================================================================
7930 //function : MergeEqualElements
7931 //purpose  : Remove all but one of elements built on the same nodes.
7932 //=======================================================================
7933
7934 void SMESH_MeshEditor::MergeEqualElements()
7935 {
7936   TIDSortedElemSet aMeshElements; /* empty input ==
7937                                      to merge equal elements in the whole mesh */
7938   TListOfListOfElementsID aGroupsOfElementsID;
7939   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7940   MergeElements( aGroupsOfElementsID );
7941 }
7942
7943 //=======================================================================
7944 //function : findAdjacentFace
7945 //purpose  :
7946 //=======================================================================
7947
7948 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7949                                                 const SMDS_MeshNode* n2,
7950                                                 const SMDS_MeshElement* elem)
7951 {
7952   TIDSortedElemSet elemSet, avoidSet;
7953   if ( elem )
7954     avoidSet.insert ( elem );
7955   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7956 }
7957
7958 //=======================================================================
7959 //function : findSegment
7960 //purpose  : Return a mesh segment by two nodes one of which can be medium
7961 //=======================================================================
7962
7963 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7964                                            const SMDS_MeshNode* n2)
7965 {
7966   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7967   while ( it->more() )
7968   {
7969     const SMDS_MeshElement* seg = it->next();
7970     if ( seg->GetNodeIndex( n2 ) >= 0 )
7971       return seg;
7972   }
7973   return 0;
7974 }
7975
7976 //=======================================================================
7977 //function : FindFreeBorder
7978 //purpose  :
7979 //=======================================================================
7980
7981 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7982
7983 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7984                                        const SMDS_MeshNode*             theSecondNode,
7985                                        const SMDS_MeshNode*             theLastNode,
7986                                        list< const SMDS_MeshNode* > &   theNodes,
7987                                        list< const SMDS_MeshElement* >& theFaces)
7988 {
7989   if ( !theFirstNode || !theSecondNode )
7990     return false;
7991   // find border face between theFirstNode and theSecondNode
7992   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7993   if ( !curElem )
7994     return false;
7995
7996   theFaces.push_back( curElem );
7997   theNodes.push_back( theFirstNode );
7998   theNodes.push_back( theSecondNode );
7999
8000   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8001   //TIDSortedElemSet foundElems;
8002   bool needTheLast = ( theLastNode != 0 );
8003
8004   vector<const SMDS_MeshNode*> nodes;
8005   
8006   while ( nStart != theLastNode ) {
8007     if ( nStart == theFirstNode )
8008       return !needTheLast;
8009
8010     // find all free border faces sharing nStart
8011
8012     list< const SMDS_MeshElement* > curElemList;
8013     list< const SMDS_MeshNode* >    nStartList;
8014     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8015     while ( invElemIt->more() ) {
8016       const SMDS_MeshElement* e = invElemIt->next();
8017       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8018       {
8019         // get nodes
8020         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8021                       SMDS_MeshElement::iterator() );
8022         nodes.push_back( nodes[ 0 ]);
8023
8024         // check 2 links
8025         int iNode = 0, nbNodes = nodes.size() - 1;
8026         for ( iNode = 0; iNode < nbNodes; iNode++ )
8027           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8028                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8029               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8030           {
8031             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8032             curElemList.push_back( e );
8033           }
8034       }
8035     }
8036     // analyse the found
8037
8038     int nbNewBorders = curElemList.size();
8039     if ( nbNewBorders == 0 ) {
8040       // no free border furthermore
8041       return !needTheLast;
8042     }
8043     else if ( nbNewBorders == 1 ) {
8044       // one more element found
8045       nIgnore = nStart;
8046       nStart = nStartList.front();
8047       curElem = curElemList.front();
8048       theFaces.push_back( curElem );
8049       theNodes.push_back( nStart );
8050     }
8051     else {
8052       // several continuations found
8053       list< const SMDS_MeshElement* >::iterator curElemIt;
8054       list< const SMDS_MeshNode* >::iterator nStartIt;
8055       // check if one of them reached the last node
8056       if ( needTheLast ) {
8057         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8058              curElemIt!= curElemList.end();
8059              curElemIt++, nStartIt++ )
8060           if ( *nStartIt == theLastNode ) {
8061             theFaces.push_back( *curElemIt );
8062             theNodes.push_back( *nStartIt );
8063             return true;
8064           }
8065       }
8066       // find the best free border by the continuations
8067       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8068       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8069       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8070            curElemIt!= curElemList.end();
8071            curElemIt++, nStartIt++ )
8072       {
8073         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8074         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8075         // find one more free border
8076         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8077           cNL->clear();
8078           cFL->clear();
8079         }
8080         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8081           // choice: clear a worse one
8082           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8083           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8084           contNodes[ iWorse ].clear();
8085           contFaces[ iWorse ].clear();
8086         }
8087       }
8088       if ( contNodes[0].empty() && contNodes[1].empty() )
8089         return false;
8090
8091       // push_back the best free border
8092       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8093       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8094       //theNodes.pop_back(); // remove nIgnore
8095       theNodes.pop_back(); // remove nStart
8096       //theFaces.pop_back(); // remove curElem
8097       theNodes.splice( theNodes.end(), *cNL );
8098       theFaces.splice( theFaces.end(), *cFL );
8099       return true;
8100
8101     } // several continuations found
8102   } // while ( nStart != theLastNode )
8103
8104   return true;
8105 }
8106
8107 //=======================================================================
8108 //function : CheckFreeBorderNodes
8109 //purpose  : Return true if the tree nodes are on a free border
8110 //=======================================================================
8111
8112 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8113                                             const SMDS_MeshNode* theNode2,
8114                                             const SMDS_MeshNode* theNode3)
8115 {
8116   list< const SMDS_MeshNode* > nodes;
8117   list< const SMDS_MeshElement* > faces;
8118   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8119 }
8120
8121 //=======================================================================
8122 //function : SewFreeBorder
8123 //purpose  :
8124 //warning  : for border-to-side sewing theSideSecondNode is considered as
8125 //           the last side node and theSideThirdNode is not used
8126 //=======================================================================
8127
8128 SMESH_MeshEditor::Sew_Error
8129 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8130                                  const SMDS_MeshNode* theBordSecondNode,
8131                                  const SMDS_MeshNode* theBordLastNode,
8132                                  const SMDS_MeshNode* theSideFirstNode,
8133                                  const SMDS_MeshNode* theSideSecondNode,
8134                                  const SMDS_MeshNode* theSideThirdNode,
8135                                  const bool           theSideIsFreeBorder,
8136                                  const bool           toCreatePolygons,
8137                                  const bool           toCreatePolyedrs)
8138 {
8139   ClearLastCreated();
8140
8141   Sew_Error aResult = SEW_OK;
8142
8143   // ====================================
8144   //    find side nodes and elements
8145   // ====================================
8146
8147   list< const SMDS_MeshNode* >    nSide[ 2 ];
8148   list< const SMDS_MeshElement* > eSide[ 2 ];
8149   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8150   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8151
8152   // Free border 1
8153   // --------------
8154   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8155                       nSide[0], eSide[0])) {
8156     MESSAGE(" Free Border 1 not found " );
8157     aResult = SEW_BORDER1_NOT_FOUND;
8158   }
8159   if (theSideIsFreeBorder) {
8160     // Free border 2
8161     // --------------
8162     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8163                         nSide[1], eSide[1])) {
8164       MESSAGE(" Free Border 2 not found " );
8165       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8166     }
8167   }
8168   if ( aResult != SEW_OK )
8169     return aResult;
8170
8171   if (!theSideIsFreeBorder) {
8172     // Side 2
8173     // --------------
8174
8175     // -------------------------------------------------------------------------
8176     // Algo:
8177     // 1. If nodes to merge are not coincident, move nodes of the free border
8178     //    from the coord sys defined by the direction from the first to last
8179     //    nodes of the border to the correspondent sys of the side 2
8180     // 2. On the side 2, find the links most co-directed with the correspondent
8181     //    links of the free border
8182     // -------------------------------------------------------------------------
8183
8184     // 1. Since sewing may break if there are volumes to split on the side 2,
8185     //    we won't move nodes but just compute new coordinates for them
8186     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8187     TNodeXYZMap nBordXYZ;
8188     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8189     list< const SMDS_MeshNode* >::iterator nBordIt;
8190
8191     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8192     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8193     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8194     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8195     double tol2 = 1.e-8;
8196     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8197     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8198       // Need node movement.
8199
8200       // find X and Z axes to create trsf
8201       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8202       gp_Vec X = Zs ^ Zb;
8203       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8204         // Zb || Zs
8205         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8206
8207       // coord systems
8208       gp_Ax3 toBordAx( Pb1, Zb, X );
8209       gp_Ax3 fromSideAx( Ps1, Zs, X );
8210       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8211       // set trsf
8212       gp_Trsf toBordSys, fromSide2Sys;
8213       toBordSys.SetTransformation( toBordAx );
8214       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8215       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8216
8217       // move
8218       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8219         const SMDS_MeshNode* n = *nBordIt;
8220         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8221         toBordSys.Transforms( xyz );
8222         fromSide2Sys.Transforms( xyz );
8223         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8224       }
8225     }
8226     else {
8227       // just insert nodes XYZ in the nBordXYZ map
8228       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8229         const SMDS_MeshNode* n = *nBordIt;
8230         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8231       }
8232     }
8233
8234     // 2. On the side 2, find the links most co-directed with the correspondent
8235     //    links of the free border
8236
8237     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8238     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8239     sideNodes.push_back( theSideFirstNode );
8240
8241     bool hasVolumes = false;
8242     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8243     set<long> foundSideLinkIDs, checkedLinkIDs;
8244     SMDS_VolumeTool volume;
8245     //const SMDS_MeshNode* faceNodes[ 4 ];
8246
8247     const SMDS_MeshNode*    sideNode;
8248     const SMDS_MeshElement* sideElem  = 0;
8249     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8250     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8251     nBordIt = bordNodes.begin();
8252     nBordIt++;
8253     // border node position and border link direction to compare with
8254     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8255     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8256     // choose next side node by link direction or by closeness to
8257     // the current border node:
8258     bool searchByDir = ( *nBordIt != theBordLastNode );
8259     do {
8260       // find the next node on the Side 2
8261       sideNode = 0;
8262       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8263       long linkID;
8264       checkedLinkIDs.clear();
8265       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8266
8267       // loop on inverse elements of current node (prevSideNode) on the Side 2
8268       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8269       while ( invElemIt->more() )
8270       {
8271         const SMDS_MeshElement* elem = invElemIt->next();
8272         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8273         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8274         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8275         bool isVolume = volume.Set( elem );
8276         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8277         if ( isVolume ) // --volume
8278           hasVolumes = true;
8279         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8280           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8281           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8282           while ( nIt->more() ) {
8283             nodes[ iNode ] = cast2Node( nIt->next() );
8284             if ( nodes[ iNode++ ] == prevSideNode )
8285               iPrevNode = iNode - 1;
8286           }
8287           // there are 2 links to check
8288           nbNodes = 2;
8289         }
8290         else // --edge
8291           continue;
8292         // loop on links, to be precise, on the second node of links
8293         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8294           const SMDS_MeshNode* n = nodes[ iNode ];
8295           if ( isVolume ) {
8296             if ( !volume.IsLinked( n, prevSideNode ))
8297               continue;
8298           }
8299           else {
8300             if ( iNode ) // a node before prevSideNode
8301               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8302             else         // a node after prevSideNode
8303               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8304           }
8305           // check if this link was already used
8306           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8307           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8308           if (!isJustChecked &&
8309               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8310           {
8311             // test a link geometrically
8312             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8313             bool linkIsBetter = false;
8314             double dot = 0.0, dist = 0.0;
8315             if ( searchByDir ) { // choose most co-directed link
8316               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8317               linkIsBetter = ( dot > maxDot );
8318             }
8319             else { // choose link with the node closest to bordPos
8320               dist = ( nextXYZ - bordPos ).SquareModulus();
8321               linkIsBetter = ( dist < minDist );
8322             }
8323             if ( linkIsBetter ) {
8324               maxDot = dot;
8325               minDist = dist;
8326               linkID = iLink;
8327               sideNode = n;
8328               sideElem = elem;
8329             }
8330           }
8331         }
8332       } // loop on inverse elements of prevSideNode
8333
8334       if ( !sideNode ) {
8335         MESSAGE(" Can't find path by links of the Side 2 ");
8336         return SEW_BAD_SIDE_NODES;
8337       }
8338       sideNodes.push_back( sideNode );
8339       sideElems.push_back( sideElem );
8340       foundSideLinkIDs.insert ( linkID );
8341       prevSideNode = sideNode;
8342
8343       if ( *nBordIt == theBordLastNode )
8344         searchByDir = false;
8345       else {
8346         // find the next border link to compare with
8347         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8348         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8349         // move to next border node if sideNode is before forward border node (bordPos)
8350         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8351           prevBordNode = *nBordIt;
8352           nBordIt++;
8353           bordPos = nBordXYZ[ *nBordIt ];
8354           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8355           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8356         }
8357       }
8358     }
8359     while ( sideNode != theSideSecondNode );
8360
8361     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8362       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8363       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8364     }
8365   } // end nodes search on the side 2
8366
8367   // ============================
8368   // sew the border to the side 2
8369   // ============================
8370
8371   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8372   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8373
8374   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8375   if ( toMergeConformal && toCreatePolygons )
8376   {
8377     // do not merge quadrangles if polygons are OK (IPAL0052824)
8378     eIt[0] = eSide[0].begin();
8379     eIt[1] = eSide[1].begin();
8380     bool allQuads[2] = { true, true };
8381     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8382       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8383         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8384     }
8385     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8386   }
8387
8388   TListOfListOfNodes nodeGroupsToMerge;
8389   if (( toMergeConformal ) ||
8390       ( theSideIsFreeBorder && !theSideThirdNode )) {
8391
8392     // all nodes are to be merged
8393
8394     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8395          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8396          nIt[0]++, nIt[1]++ )
8397     {
8398       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8399       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8400       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8401     }
8402   }
8403   else {
8404
8405     // insert new nodes into the border and the side to get equal nb of segments
8406
8407     // get normalized parameters of nodes on the borders
8408     vector< double > param[ 2 ];
8409     param[0].resize( maxNbNodes );
8410     param[1].resize( maxNbNodes );
8411     int iNode, iBord;
8412     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8413       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8414       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8415       const SMDS_MeshNode* nPrev = *nIt;
8416       double bordLength = 0;
8417       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8418         const SMDS_MeshNode* nCur = *nIt;
8419         gp_XYZ segment (nCur->X() - nPrev->X(),
8420                         nCur->Y() - nPrev->Y(),
8421                         nCur->Z() - nPrev->Z());
8422         double segmentLen = segment.Modulus();
8423         bordLength += segmentLen;
8424         param[ iBord ][ iNode ] = bordLength;
8425         nPrev = nCur;
8426       }
8427       // normalize within [0,1]
8428       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8429         param[ iBord ][ iNode ] /= bordLength;
8430       }
8431     }
8432
8433     // loop on border segments
8434     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8435     int i[ 2 ] = { 0, 0 };
8436     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8437     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8438
8439     // element can be split while iterating on border if it has two edges in the border
8440     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8441     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8442
8443     TElemOfNodeListMap insertMap;
8444     TElemOfNodeListMap::iterator insertMapIt;
8445     // insertMap is
8446     // key:   elem to insert nodes into
8447     // value: 2 nodes to insert between + nodes to be inserted
8448     do {
8449       bool next[ 2 ] = { false, false };
8450
8451       // find min adjacent segment length after sewing
8452       double nextParam = 10., prevParam = 0;
8453       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8454         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8455           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8456         if ( i[ iBord ] > 0 )
8457           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8458       }
8459       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8460       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8461       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8462
8463       // choose to insert or to merge nodes
8464       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8465       if ( Abs( du ) <= minSegLen * 0.2 ) {
8466         // merge
8467         // ------
8468         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8469         const SMDS_MeshNode* n0 = *nIt[0];
8470         const SMDS_MeshNode* n1 = *nIt[1];
8471         nodeGroupsToMerge.back().push_back( n1 );
8472         nodeGroupsToMerge.back().push_back( n0 );
8473         // position of node of the border changes due to merge
8474         param[ 0 ][ i[0] ] += du;
8475         // move n1 for the sake of elem shape evaluation during insertion.
8476         // n1 will be removed by MergeNodes() anyway
8477         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8478         next[0] = next[1] = true;
8479       }
8480       else {
8481         // insert
8482         // ------
8483         int intoBord = ( du < 0 ) ? 0 : 1;
8484         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8485         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8486         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8487         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8488         if ( intoBord == 1 ) {
8489           // move node of the border to be on a link of elem of the side
8490           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8491           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8492           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8493           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8494         }
8495         elemReplaceMapIt = elemReplaceMap.find( elem );
8496         if ( elemReplaceMapIt != elemReplaceMap.end() )
8497           elem = elemReplaceMapIt->second;
8498
8499         insertMapIt = insertMap.find( elem );
8500         bool  notFound = ( insertMapIt == insertMap.end() );
8501         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8502         if ( otherLink ) {
8503           // insert into another link of the same element:
8504           // 1. perform insertion into the other link of the elem
8505           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8506           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8507           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8508           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8509           // 2. perform insertion into the link of adjacent faces
8510           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8511             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8512           }
8513           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8514             InsertNodesIntoLink( seg, n12, n22, nodeList );
8515           }
8516           if (toCreatePolyedrs) {
8517             // perform insertion into the links of adjacent volumes
8518             UpdateVolumes(n12, n22, nodeList);
8519           }
8520           // 3. find an element appeared on n1 and n2 after the insertion
8521           insertMap.erase( insertMapIt );
8522           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8523           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8524           elem = elem2;
8525         }
8526         if ( notFound || otherLink ) {
8527           // add element and nodes of the side into the insertMap
8528           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8529           (*insertMapIt).second.push_back( n1 );
8530           (*insertMapIt).second.push_back( n2 );
8531         }
8532         // add node to be inserted into elem
8533         (*insertMapIt).second.push_back( nIns );
8534         next[ 1 - intoBord ] = true;
8535       }
8536
8537       // go to the next segment
8538       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539         if ( next[ iBord ] ) {
8540           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8541             eIt[ iBord ]++;
8542           nPrev[ iBord ] = *nIt[ iBord ];
8543           nIt[ iBord ]++; i[ iBord ]++;
8544         }
8545       }
8546     }
8547     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8548
8549     // perform insertion of nodes into elements
8550
8551     for (insertMapIt = insertMap.begin();
8552          insertMapIt != insertMap.end();
8553          insertMapIt++ )
8554     {
8555       const SMDS_MeshElement* elem = (*insertMapIt).first;
8556       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8557       if ( nodeList.size() < 3 ) continue;
8558       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8559       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8560
8561       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8562
8563       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8564         InsertNodesIntoLink( seg, n1, n2, nodeList );
8565       }
8566
8567       if ( !theSideIsFreeBorder ) {
8568         // look for and insert nodes into the faces adjacent to elem
8569         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8570           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8571         }
8572       }
8573       if (toCreatePolyedrs) {
8574         // perform insertion into the links of adjacent volumes
8575         UpdateVolumes(n1, n2, nodeList);
8576       }
8577     }
8578   } // end: insert new nodes
8579
8580   MergeNodes ( nodeGroupsToMerge );
8581
8582
8583   // Remove coincident segments
8584
8585   // get new segments
8586   TIDSortedElemSet segments;
8587   SMESH_SequenceOfElemPtr newFaces;
8588   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8589   {
8590     if ( !myLastCreatedElems[i] ) continue;
8591     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8592       segments.insert( segments.end(), myLastCreatedElems[i] );
8593     else
8594       newFaces.push_back( myLastCreatedElems[i] );
8595   }
8596   // get segments adjacent to merged nodes
8597   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8598   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8599   {
8600     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8601     if ( nodes.front()->IsNull() ) continue;
8602     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8603     while ( segIt->more() )
8604       segments.insert( segIt->next() );
8605   }
8606
8607   // find coincident
8608   TListOfListOfElementsID equalGroups;
8609   if ( !segments.empty() )
8610     FindEqualElements( segments, equalGroups );
8611   if ( !equalGroups.empty() )
8612   {
8613     // remove from segments those that will be removed
8614     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8615     for ( ; itGroups != equalGroups.end(); ++itGroups )
8616     {
8617       list< smIdType >& group = *itGroups;
8618       list< smIdType >::iterator id = group.begin();
8619       for ( ++id; id != group.end(); ++id )
8620         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8621           segments.erase( seg );
8622     }
8623     // remove equal segments
8624     MergeElements( equalGroups );
8625
8626     // restore myLastCreatedElems
8627     myLastCreatedElems = newFaces;
8628     TIDSortedElemSet::iterator seg = segments.begin();
8629     for ( ; seg != segments.end(); ++seg )
8630       myLastCreatedElems.push_back( *seg );
8631   }
8632
8633   return aResult;
8634 }
8635
8636 //=======================================================================
8637 //function : InsertNodesIntoLink
8638 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8639 //           and theBetweenNode2 and split theElement
8640 //=======================================================================
8641
8642 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8643                                            const SMDS_MeshNode*        theBetweenNode1,
8644                                            const SMDS_MeshNode*        theBetweenNode2,
8645                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8646                                            const bool                  toCreatePoly)
8647 {
8648   if ( !theElement ) return;
8649
8650   SMESHDS_Mesh *aMesh = GetMeshDS();
8651   vector<const SMDS_MeshElement*> newElems;
8652
8653   if ( theElement->GetType() == SMDSAbs_Edge )
8654   {
8655     theNodesToInsert.push_front( theBetweenNode1 );
8656     theNodesToInsert.push_back ( theBetweenNode2 );
8657     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8658     const SMDS_MeshNode* n1 = *n;
8659     for ( ++n; n != theNodesToInsert.end(); ++n )
8660     {
8661       const SMDS_MeshNode* n2 = *n;
8662       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8663         AddToSameGroups( seg, theElement, aMesh );
8664       else
8665         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8666       n1 = n2;
8667     }
8668     theNodesToInsert.pop_front();
8669     theNodesToInsert.pop_back();
8670
8671     if ( theElement->IsQuadratic() ) // add a not split part
8672     {
8673       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8674                                           theElement->end_nodes() );
8675       int iOther = 0, nbN = nodes.size();
8676       for ( ; iOther < nbN; ++iOther )
8677         if ( nodes[iOther] != theBetweenNode1 &&
8678              nodes[iOther] != theBetweenNode2 )
8679           break;
8680       if      ( iOther == 0 )
8681       {
8682         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8683           AddToSameGroups( seg, theElement, aMesh );
8684         else
8685           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8686       }
8687       else if ( iOther == 2 )
8688       {
8689         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8690           AddToSameGroups( seg, theElement, aMesh );
8691         else
8692           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8693       }
8694     }
8695     // treat new elements
8696     for ( size_t i = 0; i < newElems.size(); ++i )
8697       if ( newElems[i] )
8698       {
8699         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8700         myLastCreatedElems.push_back( newElems[i] );
8701       }
8702     ReplaceElemInGroups( theElement, newElems, aMesh );
8703     aMesh->RemoveElement( theElement );
8704     return;
8705
8706   } // if ( theElement->GetType() == SMDSAbs_Edge )
8707
8708   const SMDS_MeshElement* theFace = theElement;
8709   if ( theFace->GetType() != SMDSAbs_Face ) return;
8710
8711   // find indices of 2 link nodes and of the rest nodes
8712   int iNode = 0, il1, il2, i3, i4;
8713   il1 = il2 = i3 = i4 = -1;
8714   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8715
8716   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8717   while ( nodeIt->more() ) {
8718     const SMDS_MeshNode* n = nodeIt->next();
8719     if ( n == theBetweenNode1 )
8720       il1 = iNode;
8721     else if ( n == theBetweenNode2 )
8722       il2 = iNode;
8723     else if ( i3 < 0 )
8724       i3 = iNode;
8725     else
8726       i4 = iNode;
8727     nodes[ iNode++ ] = n;
8728   }
8729   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8730     return ;
8731
8732   // arrange link nodes to go one after another regarding the face orientation
8733   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8734   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8735   if ( reverse ) {
8736     iNode = il1;
8737     il1 = il2;
8738     il2 = iNode;
8739     aNodesToInsert.reverse();
8740   }
8741   // check that not link nodes of a quadrangles are in good order
8742   int nbFaceNodes = theFace->NbNodes();
8743   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8744     iNode = i3;
8745     i3 = i4;
8746     i4 = iNode;
8747   }
8748
8749   if (toCreatePoly || theFace->IsPoly()) {
8750
8751     iNode = 0;
8752     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8753
8754     // add nodes of face up to first node of link
8755     bool isFLN = false;
8756     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8757     while ( nodeIt->more() && !isFLN ) {
8758       const SMDS_MeshNode* n = nodeIt->next();
8759       poly_nodes[iNode++] = n;
8760       isFLN = ( n == nodes[il1] );
8761     }
8762     // add nodes to insert
8763     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8764     for (; nIt != aNodesToInsert.end(); nIt++) {
8765       poly_nodes[iNode++] = *nIt;
8766     }
8767     // add nodes of face starting from last node of link
8768     while ( nodeIt->more() ) {
8769       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8770       poly_nodes[iNode++] = n;
8771     }
8772
8773     // make a new face
8774     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8775   }
8776
8777   else if ( !theFace->IsQuadratic() )
8778   {
8779     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8780     int nbLinkNodes = 2 + aNodesToInsert.size();
8781     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8782     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8783     linkNodes[ 0 ] = nodes[ il1 ];
8784     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8785     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8786     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8787       linkNodes[ iNode++ ] = *nIt;
8788     }
8789     // decide how to split a quadrangle: compare possible variants
8790     // and choose which of splits to be a quadrangle
8791     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8792     if ( nbFaceNodes == 3 ) {
8793       iBestQuad = nbSplits;
8794       i4 = i3;
8795     }
8796     else if ( nbFaceNodes == 4 ) {
8797       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8798       double aBestRate = DBL_MAX;
8799       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8800         i1 = 0; i2 = 1;
8801         double aBadRate = 0;
8802         // evaluate elements quality
8803         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8804           if ( iSplit == iQuad ) {
8805             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8806                                    linkNodes[ i2++ ],
8807                                    nodes[ i3 ],
8808                                    nodes[ i4 ]);
8809             aBadRate += getBadRate( &quad, aCrit );
8810           }
8811           else {
8812             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8813                                    linkNodes[ i2++ ],
8814                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8815             aBadRate += getBadRate( &tria, aCrit );
8816           }
8817         }
8818         // choice
8819         if ( aBadRate < aBestRate ) {
8820           iBestQuad = iQuad;
8821           aBestRate = aBadRate;
8822         }
8823       }
8824     }
8825
8826     // create new elements
8827     i1 = 0; i2 = 1;
8828     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8829     {
8830       if ( iSplit == iBestQuad )
8831         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8832                                             linkNodes[ i2++ ],
8833                                             nodes[ i3 ],
8834                                             nodes[ i4 ]));
8835       else
8836         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8837                                             linkNodes[ i2++ ],
8838                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8839     }
8840
8841     const SMDS_MeshNode* newNodes[ 4 ];
8842     newNodes[ 0 ] = linkNodes[ i1 ];
8843     newNodes[ 1 ] = linkNodes[ i2 ];
8844     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8845     newNodes[ 3 ] = nodes[ i4 ];
8846     if (iSplit == iBestQuad)
8847       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8848     else
8849       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8850
8851   } // end if(!theFace->IsQuadratic())
8852
8853   else { // theFace is quadratic
8854     // we have to split theFace on simple triangles and one simple quadrangle
8855     int tmp = il1/2;
8856     int nbshift = tmp*2;
8857     // shift nodes in nodes[] by nbshift
8858     int i,j;
8859     for(i=0; i<nbshift; i++) {
8860       const SMDS_MeshNode* n = nodes[0];
8861       for(j=0; j<nbFaceNodes-1; j++) {
8862         nodes[j] = nodes[j+1];
8863       }
8864       nodes[nbFaceNodes-1] = n;
8865     }
8866     il1 = il1 - nbshift;
8867     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8868     //   n0      n1     n2    n0      n1     n2
8869     //     +-----+-----+        +-----+-----+
8870     //      \         /         |           |
8871     //       \       /          |           |
8872     //      n5+     +n3       n7+           +n3
8873     //         \   /            |           |
8874     //          \ /             |           |
8875     //           +              +-----+-----+
8876     //           n4           n6      n5     n4
8877
8878     // create new elements
8879     int n1,n2,n3;
8880     if ( nbFaceNodes == 6 ) { // quadratic triangle
8881       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8882       if ( theFace->IsMediumNode(nodes[il1]) ) {
8883         // create quadrangle
8884         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8885         n1 = 1;
8886         n2 = 2;
8887         n3 = 3;
8888       }
8889       else {
8890         // create quadrangle
8891         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8892         n1 = 0;
8893         n2 = 1;
8894         n3 = 5;
8895       }
8896     }
8897     else { // nbFaceNodes==8 - quadratic quadrangle
8898       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8899       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8900       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8901       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8902         // create quadrangle
8903         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8904         n1 = 1;
8905         n2 = 2;
8906         n3 = 3;
8907       }
8908       else {
8909         // create quadrangle
8910         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8911         n1 = 0;
8912         n2 = 1;
8913         n3 = 7;
8914       }
8915     }
8916     // create needed triangles using n1,n2,n3 and inserted nodes
8917     int nbn = 2 + aNodesToInsert.size();
8918     vector<const SMDS_MeshNode*> aNodes(nbn);
8919     aNodes[0    ] = nodes[n1];
8920     aNodes[nbn-1] = nodes[n2];
8921     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923       aNodes[iNode++] = *nIt;
8924     }
8925     for ( i = 1; i < nbn; i++ )
8926       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8927   }
8928
8929   // remove the old face
8930   for ( size_t i = 0; i < newElems.size(); ++i )
8931     if ( newElems[i] )
8932     {
8933       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8934       myLastCreatedElems.push_back( newElems[i] );
8935     }
8936   ReplaceElemInGroups( theFace, newElems, aMesh );
8937   aMesh->RemoveElement(theFace);
8938
8939 } // InsertNodesIntoLink()
8940
8941 //=======================================================================
8942 //function : UpdateVolumes
8943 //purpose  :
8944 //=======================================================================
8945
8946 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8947                                       const SMDS_MeshNode*        theBetweenNode2,
8948                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8949 {
8950   ClearLastCreated();
8951
8952   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8953   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8954     const SMDS_MeshElement* elem = invElemIt->next();
8955
8956     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8957     SMDS_VolumeTool aVolume (elem);
8958     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8959       continue;
8960
8961     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8962     int iface, nbFaces = aVolume.NbFaces();
8963     vector<const SMDS_MeshNode *> poly_nodes;
8964     vector<int> quantities (nbFaces);
8965
8966     for (iface = 0; iface < nbFaces; iface++) {
8967       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8968       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8969       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8970
8971       for (int inode = 0; inode < nbFaceNodes; inode++) {
8972         poly_nodes.push_back(faceNodes[inode]);
8973
8974         if (nbInserted == 0) {
8975           if (faceNodes[inode] == theBetweenNode1) {
8976             if (faceNodes[inode + 1] == theBetweenNode2) {
8977               nbInserted = theNodesToInsert.size();
8978
8979               // add nodes to insert
8980               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8981               for (; nIt != theNodesToInsert.end(); nIt++) {
8982                 poly_nodes.push_back(*nIt);
8983               }
8984             }
8985           }
8986           else if (faceNodes[inode] == theBetweenNode2) {
8987             if (faceNodes[inode + 1] == theBetweenNode1) {
8988               nbInserted = theNodesToInsert.size();
8989
8990               // add nodes to insert in reversed order
8991               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8992               nIt--;
8993               for (; nIt != theNodesToInsert.begin(); nIt--) {
8994                 poly_nodes.push_back(*nIt);
8995               }
8996               poly_nodes.push_back(*nIt);
8997             }
8998           }
8999           else {
9000           }
9001         }
9002       }
9003       quantities[iface] = nbFaceNodes + nbInserted;
9004     }
9005
9006     // Replace the volume
9007     SMESHDS_Mesh *aMesh = GetMeshDS();
9008
9009     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9010     {
9011       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9012       myLastCreatedElems.push_back( newElem );
9013       ReplaceElemInGroups( elem, newElem, aMesh );
9014     }
9015     aMesh->RemoveElement( elem );
9016   }
9017 }
9018
9019 namespace
9020 {
9021   //================================================================================
9022   /*!
9023    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9024    */
9025   //================================================================================
9026
9027   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9028                            vector<const SMDS_MeshNode *> & nodes,
9029                            vector<int> &                   nbNodeInFaces )
9030   {
9031     nodes.clear();
9032     nbNodeInFaces.clear();
9033     SMDS_VolumeTool vTool ( elem );
9034     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9035     {
9036       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9037       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9038       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9039     }
9040   }
9041 }
9042
9043 //=======================================================================
9044 /*!
9045  * \brief Convert elements contained in a sub-mesh to quadratic
9046  * \return int - nb of checked elements
9047  */
9048 //=======================================================================
9049
9050 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9051                                                   SMESH_MesherHelper& theHelper,
9052                                                   const bool          theForce3d)
9053 {
9054   //MESSAGE("convertElemToQuadratic");
9055   smIdType nbElem = 0;
9056   if( !theSm ) return nbElem;
9057
9058   vector<int> nbNodeInFaces;
9059   vector<const SMDS_MeshNode *> nodes;
9060   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9061   while(ElemItr->more())
9062   {
9063     nbElem++;
9064     const SMDS_MeshElement* elem = ElemItr->next();
9065     if( !elem ) continue;
9066
9067     // analyse a necessity of conversion
9068     const SMDSAbs_ElementType aType = elem->GetType();
9069     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9070       continue;
9071     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9072     bool hasCentralNodes = false;
9073     if ( elem->IsQuadratic() )
9074     {
9075       bool alreadyOK;
9076       switch ( aGeomType ) {
9077       case SMDSEntity_Quad_Triangle:
9078       case SMDSEntity_Quad_Quadrangle:
9079       case SMDSEntity_Quad_Hexa:
9080       case SMDSEntity_Quad_Penta:
9081         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9082
9083       case SMDSEntity_BiQuad_Triangle:
9084       case SMDSEntity_BiQuad_Quadrangle:
9085       case SMDSEntity_TriQuad_Hexa:
9086       case SMDSEntity_BiQuad_Penta:
9087         alreadyOK = theHelper.GetIsBiQuadratic();
9088         hasCentralNodes = true;
9089         break;
9090       default:
9091         alreadyOK = true;
9092       }
9093       // take into account already present medium nodes
9094       switch ( aType ) {
9095       case SMDSAbs_Volume:
9096         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9097       case SMDSAbs_Face:
9098         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9099       case SMDSAbs_Edge:
9100         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9101       default:;
9102       }
9103       if ( alreadyOK )
9104         continue;
9105     }
9106     // get elem data needed to re-create it
9107     //
9108     const smIdType id = elem->GetID();
9109     const int nbNodes = elem->NbCornerNodes();
9110     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9111     if ( aGeomType == SMDSEntity_Polyhedra )
9112       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9113     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9114       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9115
9116     // remove a linear element
9117     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9118
9119     // remove central nodes of biquadratic elements (biquad->quad conversion)
9120     if ( hasCentralNodes )
9121       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9122         if ( nodes[i]->NbInverseElements() == 0 )
9123           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9124
9125     const SMDS_MeshElement* NewElem = 0;
9126
9127     switch( aType )
9128     {
9129     case SMDSAbs_Edge :
9130     {
9131       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9132       break;
9133     }
9134     case SMDSAbs_Face :
9135     {
9136       switch(nbNodes)
9137       {
9138       case 3:
9139         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9140         break;
9141       case 4:
9142         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9143         break;
9144       default:
9145         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9146       }
9147       break;
9148     }
9149     case SMDSAbs_Volume :
9150     {
9151       switch( aGeomType )
9152       {
9153       case SMDSEntity_Tetra:
9154         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9155         break;
9156       case SMDSEntity_Pyramid:
9157         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9158         break;
9159       case SMDSEntity_Penta:
9160       case SMDSEntity_Quad_Penta:
9161       case SMDSEntity_BiQuad_Penta:
9162         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9163         break;
9164       case SMDSEntity_Hexa:
9165       case SMDSEntity_Quad_Hexa:
9166       case SMDSEntity_TriQuad_Hexa:
9167         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9168                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9169         break;
9170       case SMDSEntity_Hexagonal_Prism:
9171       default:
9172         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9173       }
9174       break;
9175     }
9176     default :
9177       continue;
9178     }
9179     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9180     if( NewElem && NewElem->getshapeId() < 1 )
9181       theSm->AddElement( NewElem );
9182   }
9183   return nbElem;
9184 }
9185 //=======================================================================
9186 //function : ConvertToQuadratic
9187 //purpose  :
9188 //=======================================================================
9189
9190 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9191 {
9192   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9193   SMESHDS_Mesh* meshDS = GetMeshDS();
9194
9195   SMESH_MesherHelper aHelper(*myMesh);
9196
9197   aHelper.SetIsQuadratic( true );
9198   aHelper.SetIsBiQuadratic( theToBiQuad );
9199   aHelper.SetElementsOnShape(true);
9200   aHelper.ToFixNodeParameters( true );
9201
9202   // convert elements assigned to sub-meshes
9203   smIdType nbCheckedElems = 0;
9204   if ( myMesh->HasShapeToMesh() )
9205   {
9206     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9207     {
9208       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9209       while ( smIt->more() ) {
9210         SMESH_subMesh* sm = smIt->next();
9211         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9212           aHelper.SetSubShape( sm->GetSubShape() );
9213           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9214         }
9215       }
9216     }
9217   }
9218
9219   // convert elements NOT assigned to sub-meshes
9220   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9221   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9222   {
9223     aHelper.SetElementsOnShape(false);
9224     SMESHDS_SubMesh *smDS = 0;
9225
9226     // convert edges
9227     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9228     while( aEdgeItr->more() )
9229     {
9230       const SMDS_MeshEdge* edge = aEdgeItr->next();
9231       if ( !edge->IsQuadratic() )
9232       {
9233         smIdType                  id = edge->GetID();
9234         const SMDS_MeshNode* n1 = edge->GetNode(0);
9235         const SMDS_MeshNode* n2 = edge->GetNode(1);
9236
9237         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9238
9239         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9240         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9241       }
9242       else
9243       {
9244         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9245       }
9246     }
9247
9248     // convert faces
9249     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9250     while( aFaceItr->more() )
9251     {
9252       const SMDS_MeshFace* face = aFaceItr->next();
9253       if ( !face ) continue;
9254       
9255       const SMDSAbs_EntityType type = face->GetEntityType();
9256       bool alreadyOK;
9257       switch( type )
9258       {
9259       case SMDSEntity_Quad_Triangle:
9260       case SMDSEntity_Quad_Quadrangle:
9261         alreadyOK = !theToBiQuad;
9262         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9263         break;
9264       case SMDSEntity_BiQuad_Triangle:
9265       case SMDSEntity_BiQuad_Quadrangle:
9266         alreadyOK = theToBiQuad;
9267         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9268         break;
9269       default: alreadyOK = false;
9270       }
9271       if ( alreadyOK )
9272         continue;
9273
9274       const smIdType id = face->GetID();
9275       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9276
9277       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9278
9279       SMDS_MeshFace * NewFace = 0;
9280       switch( type )
9281       {
9282       case SMDSEntity_Triangle:
9283       case SMDSEntity_Quad_Triangle:
9284       case SMDSEntity_BiQuad_Triangle:
9285         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9286         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9287           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9288         break;
9289
9290       case SMDSEntity_Quadrangle:
9291       case SMDSEntity_Quad_Quadrangle:
9292       case SMDSEntity_BiQuad_Quadrangle:
9293         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9294         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9295           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9296         break;
9297
9298       default:;
9299         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9300       }
9301       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9302     }
9303
9304     // convert volumes
9305     vector<int> nbNodeInFaces;
9306     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9307     while(aVolumeItr->more())
9308     {
9309       const SMDS_MeshVolume* volume = aVolumeItr->next();
9310       if ( !volume ) continue;
9311
9312       const SMDSAbs_EntityType type = volume->GetEntityType();
9313       if ( volume->IsQuadratic() )
9314       {
9315         bool alreadyOK;
9316         switch ( type )
9317         {
9318         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9319         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9320         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9321         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9322         default:                      alreadyOK = true;
9323         }
9324         if ( alreadyOK )
9325         {
9326           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9327           continue;
9328         }
9329       }
9330       const smIdType id = volume->GetID();
9331       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9332       if ( type == SMDSEntity_Polyhedra )
9333         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9334       else if ( type == SMDSEntity_Hexagonal_Prism )
9335         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9336
9337       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9338
9339       SMDS_MeshVolume * NewVolume = 0;
9340       switch ( type )
9341       {
9342       case SMDSEntity_Tetra:
9343         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9344         break;
9345       case SMDSEntity_Hexa:
9346       case SMDSEntity_Quad_Hexa:
9347       case SMDSEntity_TriQuad_Hexa:
9348         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9349                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9350         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9351           if ( nodes[i]->NbInverseElements() == 0 )
9352             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9353         break;
9354       case SMDSEntity_Pyramid:
9355         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9356                                       nodes[3], nodes[4], id, theForce3d);
9357         break;
9358       case SMDSEntity_Penta:
9359       case SMDSEntity_Quad_Penta:
9360       case SMDSEntity_BiQuad_Penta:
9361         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9362                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9363         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9364           if ( nodes[i]->NbInverseElements() == 0 )
9365             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9366         break;
9367       case SMDSEntity_Hexagonal_Prism:
9368       default:
9369         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9370       }
9371       ReplaceElemInGroups(volume, NewVolume, meshDS);
9372     }
9373   }
9374
9375   if ( !theForce3d )
9376   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9377     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9378     // aHelper.FixQuadraticElements(myError);
9379     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9380   }
9381 }
9382
9383 //================================================================================
9384 /*!
9385  * \brief Makes given elements quadratic
9386  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9387  *  \param theElements - elements to make quadratic
9388  */
9389 //================================================================================
9390
9391 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9392                                           TIDSortedElemSet& theElements,
9393                                           const bool        theToBiQuad)
9394 {
9395   if ( theElements.empty() ) return;
9396
9397   // we believe that all theElements are of the same type
9398   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9399
9400   // get all nodes shared by theElements
9401   TIDSortedNodeSet allNodes;
9402   TIDSortedElemSet::iterator eIt = theElements.begin();
9403   for ( ; eIt != theElements.end(); ++eIt )
9404     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9405
9406   // complete theElements with elements of lower dim whose all nodes are in allNodes
9407
9408   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9409   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9410   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9411   for ( ; nIt != allNodes.end(); ++nIt )
9412   {
9413     const SMDS_MeshNode* n = *nIt;
9414     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9415     while ( invIt->more() )
9416     {
9417       const SMDS_MeshElement*      e = invIt->next();
9418       const SMDSAbs_ElementType type = e->GetType();
9419       if ( e->IsQuadratic() )
9420       {
9421         quadAdjacentElems[ type ].insert( e );
9422
9423         bool alreadyOK;
9424         switch ( e->GetEntityType() ) {
9425         case SMDSEntity_Quad_Triangle:
9426         case SMDSEntity_Quad_Quadrangle:
9427         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9428         case SMDSEntity_BiQuad_Triangle:
9429         case SMDSEntity_BiQuad_Quadrangle:
9430         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9431         default:                           alreadyOK = true;
9432         }
9433         if ( alreadyOK )
9434           continue;
9435       }
9436       if ( type >= elemType )
9437         continue; // same type or more complex linear element
9438
9439       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9440         continue; // e is already checked
9441
9442       // check nodes
9443       bool allIn = true;
9444       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9445       while ( nodeIt->more() && allIn )
9446         allIn = allNodes.count( nodeIt->next() );
9447       if ( allIn )
9448         theElements.insert(e );
9449     }
9450   }
9451
9452   SMESH_MesherHelper helper(*myMesh);
9453   helper.SetIsQuadratic( true );
9454   helper.SetIsBiQuadratic( theToBiQuad );
9455
9456   // add links of quadratic adjacent elements to the helper
9457
9458   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9459     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9460           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9461     {
9462       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9463     }
9464   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9465     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9466           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9467     {
9468       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9469     }
9470   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9471     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9472           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9473     {
9474       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9475     }
9476
9477   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9478
9479   SMESHDS_Mesh*  meshDS = GetMeshDS();
9480   SMESHDS_SubMesh* smDS = 0;
9481   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9482   {
9483     const SMDS_MeshElement* elem = *eIt;
9484
9485     bool alreadyOK;
9486     int nbCentralNodes = 0;
9487     switch ( elem->GetEntityType() ) {
9488       // linear convertible
9489     case SMDSEntity_Edge:
9490     case SMDSEntity_Triangle:
9491     case SMDSEntity_Quadrangle:
9492     case SMDSEntity_Tetra:
9493     case SMDSEntity_Pyramid:
9494     case SMDSEntity_Hexa:
9495     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9496       // quadratic that can become bi-quadratic
9497     case SMDSEntity_Quad_Triangle:
9498     case SMDSEntity_Quad_Quadrangle:
9499     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9500       // bi-quadratic
9501     case SMDSEntity_BiQuad_Triangle:
9502     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9503     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9504       // the rest
9505     default:                           alreadyOK = true;
9506     }
9507     if ( alreadyOK ) continue;
9508
9509     const SMDSAbs_ElementType type = elem->GetType();
9510     const smIdType              id = elem->GetID();
9511     const int              nbNodes = elem->NbCornerNodes();
9512     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9513
9514     helper.SetSubShape( elem->getshapeId() );
9515
9516     if ( !smDS || !smDS->Contains( elem ))
9517       smDS = meshDS->MeshElements( elem->getshapeId() );
9518     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9519
9520     SMDS_MeshElement * newElem = 0;
9521     switch( nbNodes )
9522     {
9523     case 4: // cases for most frequently used element types go first (for optimization)
9524       if ( type == SMDSAbs_Volume )
9525         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9526       else
9527         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9528       break;
9529     case 8:
9530       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9531                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9532       break;
9533     case 3:
9534       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9535       break;
9536     case 2:
9537       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9538       break;
9539     case 5:
9540       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9541                                  nodes[4], id, theForce3d);
9542       break;
9543     case 6:
9544       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9545                                  nodes[4], nodes[5], id, theForce3d);
9546       break;
9547     default:;
9548     }
9549     ReplaceElemInGroups( elem, newElem, meshDS);
9550     if( newElem && smDS )
9551       smDS->AddElement( newElem );
9552
9553     // remove central nodes
9554     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9555       if ( nodes[i]->NbInverseElements() == 0 )
9556         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9557
9558   } // loop on theElements
9559
9560   if ( !theForce3d )
9561   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9562     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9563     // helper.FixQuadraticElements( myError );
9564     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9565   }
9566 }
9567
9568 //=======================================================================
9569 /*!
9570  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9571  * \return smIdType - nb of checked elements
9572  */
9573 //=======================================================================
9574
9575 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9576                                           SMDS_ElemIteratorPtr theItr,
9577                                           const int            /*theShapeID*/)
9578 {
9579   smIdType nbElem = 0;
9580   SMESHDS_Mesh* meshDS = GetMeshDS();
9581   ElemFeatures elemType;
9582   vector<const SMDS_MeshNode *> nodes;
9583
9584   while( theItr->more() )
9585   {
9586     const SMDS_MeshElement* elem = theItr->next();
9587     nbElem++;
9588     if( elem && elem->IsQuadratic())
9589     {
9590       // get elem data
9591       int nbCornerNodes = elem->NbCornerNodes();
9592       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9593
9594       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9595
9596       //remove a quadratic element
9597       if ( !theSm || !theSm->Contains( elem ))
9598         theSm = meshDS->MeshElements( elem->getshapeId() );
9599       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9600
9601       // remove medium nodes
9602       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9603         if ( nodes[i]->NbInverseElements() == 0 )
9604           meshDS->RemoveFreeNode( nodes[i], theSm );
9605
9606       // add a linear element
9607       nodes.resize( nbCornerNodes );
9608       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9609       ReplaceElemInGroups(elem, newElem, meshDS);
9610       if( theSm && newElem )
9611         theSm->AddElement( newElem );
9612     }
9613   }
9614   return nbElem;
9615 }
9616
9617 //=======================================================================
9618 //function : ConvertFromQuadratic
9619 //purpose  :
9620 //=======================================================================
9621
9622 bool SMESH_MeshEditor::ConvertFromQuadratic()
9623 {
9624   smIdType nbCheckedElems = 0;
9625   if ( myMesh->HasShapeToMesh() )
9626   {
9627     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9628     {
9629       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9630       while ( smIt->more() ) {
9631         SMESH_subMesh* sm = smIt->next();
9632         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9633           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9634       }
9635     }
9636   }
9637
9638   smIdType totalNbElems =
9639     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9640   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9641   {
9642     SMESHDS_SubMesh *aSM = 0;
9643     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9644   }
9645
9646   return true;
9647 }
9648
9649 namespace
9650 {
9651   //================================================================================
9652   /*!
9653    * \brief Return true if all medium nodes of the element are in the node set
9654    */
9655   //================================================================================
9656
9657   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9658   {
9659     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9660       if ( !nodeSet.count( elem->GetNode(i) ))
9661         return false;
9662     return true;
9663   }
9664 }
9665
9666 //================================================================================
9667 /*!
9668  * \brief Makes given elements linear
9669  */
9670 //================================================================================
9671
9672 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9673 {
9674   if ( theElements.empty() ) return;
9675
9676   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9677   set<smIdType> mediumNodeIDs;
9678   TIDSortedElemSet::iterator eIt = theElements.begin();
9679   for ( ; eIt != theElements.end(); ++eIt )
9680   {
9681     const SMDS_MeshElement* e = *eIt;
9682     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9683       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9684   }
9685
9686   // replace given elements by linear ones
9687   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9688   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9689
9690   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9691   // except those elements sharing medium nodes of quadratic element whose medium nodes
9692   // are not all in mediumNodeIDs
9693
9694   // get remaining medium nodes
9695   TIDSortedNodeSet mediumNodes;
9696   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9697   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9698     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9699       mediumNodes.insert( mediumNodes.end(), n );
9700
9701   // find more quadratic elements to convert
9702   TIDSortedElemSet moreElemsToConvert;
9703   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9704   for ( ; nIt != mediumNodes.end(); ++nIt )
9705   {
9706     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9707     while ( invIt->more() )
9708     {
9709       const SMDS_MeshElement* e = invIt->next();
9710       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9711       {
9712         // find a more complex element including e and
9713         // whose medium nodes are not in mediumNodes
9714         bool complexFound = false;
9715         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9716         {
9717           SMDS_ElemIteratorPtr invIt2 =
9718             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9719           while ( invIt2->more() )
9720           {
9721             const SMDS_MeshElement* eComplex = invIt2->next();
9722             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9723             {
9724               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9725               if ( nbCommonNodes == e->NbNodes())
9726               {
9727                 complexFound = true;
9728                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9729                 break;
9730               }
9731             }
9732           }
9733         }
9734         if ( !complexFound )
9735           moreElemsToConvert.insert( e );
9736       }
9737     }
9738   }
9739   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9740   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9741 }
9742
9743 //=======================================================================
9744 //function : SewSideElements
9745 //purpose  :
9746 //=======================================================================
9747
9748 SMESH_MeshEditor::Sew_Error
9749 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9750                                    TIDSortedElemSet&    theSide2,
9751                                    const SMDS_MeshNode* theFirstNode1,
9752                                    const SMDS_MeshNode* theFirstNode2,
9753                                    const SMDS_MeshNode* theSecondNode1,
9754                                    const SMDS_MeshNode* theSecondNode2)
9755 {
9756   ClearLastCreated();
9757
9758   if ( theSide1.size() != theSide2.size() )
9759     return SEW_DIFF_NB_OF_ELEMENTS;
9760
9761   Sew_Error aResult = SEW_OK;
9762   // Algo:
9763   // 1. Build set of faces representing each side
9764   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9765   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9766
9767   // =======================================================================
9768   // 1. Build set of faces representing each side:
9769   // =======================================================================
9770   // a. build set of nodes belonging to faces
9771   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9772   // c. create temporary faces representing side of volumes if correspondent
9773   //    face does not exist
9774
9775   SMESHDS_Mesh* aMesh = GetMeshDS();
9776   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9777   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9778   TIDSortedElemSet             faceSet1, faceSet2;
9779   set<const SMDS_MeshElement*> volSet1,  volSet2;
9780   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9781   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9782   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9783   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9784   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9785   int iSide, iFace, iNode;
9786
9787   list<const SMDS_MeshElement* > tempFaceList;
9788   for ( iSide = 0; iSide < 2; iSide++ ) {
9789     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9790     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9791     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9792     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9793     set<const SMDS_MeshElement*>::iterator vIt;
9794     TIDSortedElemSet::iterator eIt;
9795     set<const SMDS_MeshNode*>::iterator    nIt;
9796
9797     // check that given nodes belong to given elements
9798     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9799     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9800     int firstIndex = -1, secondIndex = -1;
9801     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9802       const SMDS_MeshElement* elem = *eIt;
9803       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9804       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9805       if ( firstIndex > -1 && secondIndex > -1 ) break;
9806     }
9807     if ( firstIndex < 0 || secondIndex < 0 ) {
9808       // we can simply return until temporary faces created
9809       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9810     }
9811
9812     // -----------------------------------------------------------
9813     // 1a. Collect nodes of existing faces
9814     //     and build set of face nodes in order to detect missing
9815     //     faces corresponding to sides of volumes
9816     // -----------------------------------------------------------
9817
9818     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9819
9820     // loop on the given element of a side
9821     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9822       //const SMDS_MeshElement* elem = *eIt;
9823       const SMDS_MeshElement* elem = *eIt;
9824       if ( elem->GetType() == SMDSAbs_Face ) {
9825         faceSet->insert( elem );
9826         set <const SMDS_MeshNode*> faceNodeSet;
9827         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9828         while ( nodeIt->more() ) {
9829           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9830           nodeSet->insert( n );
9831           faceNodeSet.insert( n );
9832         }
9833         setOfFaceNodeSet.insert( faceNodeSet );
9834       }
9835       else if ( elem->GetType() == SMDSAbs_Volume )
9836         volSet->insert( elem );
9837     }
9838     // ------------------------------------------------------------------------------
9839     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9840     // ------------------------------------------------------------------------------
9841
9842     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9843       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9844       while ( fIt->more() ) { // loop on faces sharing a node
9845         const SMDS_MeshElement* f = fIt->next();
9846         if ( faceSet->find( f ) == faceSet->end() ) {
9847           // check if all nodes are in nodeSet and
9848           // complete setOfFaceNodeSet if they are
9849           set <const SMDS_MeshNode*> faceNodeSet;
9850           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9851           bool allInSet = true;
9852           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9853             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9854             if ( nodeSet->find( n ) == nodeSet->end() )
9855               allInSet = false;
9856             else
9857               faceNodeSet.insert( n );
9858           }
9859           if ( allInSet ) {
9860             faceSet->insert( f );
9861             setOfFaceNodeSet.insert( faceNodeSet );
9862           }
9863         }
9864       }
9865     }
9866
9867     // -------------------------------------------------------------------------
9868     // 1c. Create temporary faces representing sides of volumes if correspondent
9869     //     face does not exist
9870     // -------------------------------------------------------------------------
9871
9872     if ( !volSet->empty() ) {
9873       //int nodeSetSize = nodeSet->size();
9874
9875       // loop on given volumes
9876       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9877         SMDS_VolumeTool vol (*vIt);
9878         // loop on volume faces: find free faces
9879         // --------------------------------------
9880         list<const SMDS_MeshElement* > freeFaceList;
9881         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9882           if ( !vol.IsFreeFace( iFace ))
9883             continue;
9884           // check if there is already a face with same nodes in a face set
9885           const SMDS_MeshElement* aFreeFace = 0;
9886           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9887           int nbNodes = vol.NbFaceNodes( iFace );
9888           set <const SMDS_MeshNode*> faceNodeSet;
9889           vol.GetFaceNodes( iFace, faceNodeSet );
9890           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9891           if ( isNewFace ) {
9892             // no such a face is given but it still can exist, check it
9893             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9894             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9895           }
9896           if ( !aFreeFace ) {
9897             // create a temporary face
9898             if ( nbNodes == 3 ) {
9899               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9900               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9901             }
9902             else if ( nbNodes == 4 ) {
9903               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9904               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9905             }
9906             else {
9907               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9908               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9909               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9910             }
9911             if ( aFreeFace )
9912               tempFaceList.push_back( aFreeFace );
9913           }
9914
9915           if ( aFreeFace )
9916             freeFaceList.push_back( aFreeFace );
9917
9918         } // loop on faces of a volume
9919
9920         // choose one of several free faces of a volume
9921         // --------------------------------------------
9922         if ( freeFaceList.size() > 1 ) {
9923           // choose a face having max nb of nodes shared by other elems of a side
9924           int maxNbNodes = -1;
9925           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9926           while ( fIt != freeFaceList.end() ) { // loop on free faces
9927             int nbSharedNodes = 0;
9928             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9929             while ( nodeIt->more() ) { // loop on free face nodes
9930               const SMDS_MeshNode* n =
9931                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9932               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9933               while ( invElemIt->more() ) {
9934                 const SMDS_MeshElement* e = invElemIt->next();
9935                 nbSharedNodes += faceSet->count( e );
9936                 nbSharedNodes += elemSet->count( e );
9937               }
9938             }
9939             if ( nbSharedNodes > maxNbNodes ) {
9940               maxNbNodes = nbSharedNodes;
9941               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9942             }
9943             else if ( nbSharedNodes == maxNbNodes ) {
9944               fIt++;
9945             }
9946             else {
9947               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9948             }
9949           }
9950           if ( freeFaceList.size() > 1 )
9951           {
9952             // could not choose one face, use another way
9953             // choose a face most close to the bary center of the opposite side
9954             gp_XYZ aBC( 0., 0., 0. );
9955             set <const SMDS_MeshNode*> addedNodes;
9956             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9957             eIt = elemSet2->begin();
9958             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9959               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9960               while ( nodeIt->more() ) { // loop on free face nodes
9961                 const SMDS_MeshNode* n =
9962                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9963                 if ( addedNodes.insert( n ).second )
9964                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9965               }
9966             }
9967             aBC /= addedNodes.size();
9968             double minDist = DBL_MAX;
9969             fIt = freeFaceList.begin();
9970             while ( fIt != freeFaceList.end() ) { // loop on free faces
9971               double dist = 0;
9972               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9973               while ( nodeIt->more() ) { // loop on free face nodes
9974                 const SMDS_MeshNode* n =
9975                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9976                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9977                 dist += ( aBC - p ).SquareModulus();
9978               }
9979               if ( dist < minDist ) {
9980                 minDist = dist;
9981                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9982               }
9983               else
9984                 fIt = freeFaceList.erase( fIt++ );
9985             }
9986           }
9987         } // choose one of several free faces of a volume
9988
9989         if ( freeFaceList.size() == 1 ) {
9990           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9991           faceSet->insert( aFreeFace );
9992           // complete a node set with nodes of a found free face
9993           //           for ( iNode = 0; iNode < ; iNode++ )
9994           //             nodeSet->insert( fNodes[ iNode ] );
9995         }
9996
9997       } // loop on volumes of a side
9998
9999       //       // complete a set of faces if new nodes in a nodeSet appeared
10000       //       // ----------------------------------------------------------
10001       //       if ( nodeSetSize != nodeSet->size() ) {
10002       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10003       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10004       //           while ( fIt->more() ) { // loop on faces sharing a node
10005       //             const SMDS_MeshElement* f = fIt->next();
10006       //             if ( faceSet->find( f ) == faceSet->end() ) {
10007       //               // check if all nodes are in nodeSet and
10008       //               // complete setOfFaceNodeSet if they are
10009       //               set <const SMDS_MeshNode*> faceNodeSet;
10010       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10011       //               bool allInSet = true;
10012       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10013       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10014       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10015       //                   allInSet = false;
10016       //                 else
10017       //                   faceNodeSet.insert( n );
10018       //               }
10019       //               if ( allInSet ) {
10020       //                 faceSet->insert( f );
10021       //                 setOfFaceNodeSet.insert( faceNodeSet );
10022       //               }
10023       //             }
10024       //           }
10025       //         }
10026       //       }
10027     } // Create temporary faces, if there are volumes given
10028   } // loop on sides
10029
10030   if ( faceSet1.size() != faceSet2.size() ) {
10031     // delete temporary faces: they are in reverseElements of actual nodes
10032     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10033     //    while ( tmpFaceIt->more() )
10034     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10035     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10036     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10037     //      aMesh->RemoveElement(*tmpFaceIt);
10038     MESSAGE("Diff nb of faces");
10039     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10040   }
10041
10042   // ============================================================
10043   // 2. Find nodes to merge:
10044   //              bind a node to remove to a node to put instead
10045   // ============================================================
10046
10047   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10048   if ( theFirstNode1 != theFirstNode2 )
10049     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10050   if ( theSecondNode1 != theSecondNode2 )
10051     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10052
10053   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10054   set< long > linkIdSet; // links to process
10055   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10056
10057   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10058   list< NLink > linkList[2];
10059   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10060   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10061   // loop on links in linkList; find faces by links and append links
10062   // of the found faces to linkList
10063   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10064   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10065   {
10066     NLink link[] = { *linkIt[0], *linkIt[1] };
10067     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10068     if ( !linkIdSet.count( linkID ) )
10069       continue;
10070
10071     // by links, find faces in the face sets,
10072     // and find indices of link nodes in the found faces;
10073     // in a face set, there is only one or no face sharing a link
10074     // ---------------------------------------------------------------
10075
10076     const SMDS_MeshElement* face[] = { 0, 0 };
10077     vector<const SMDS_MeshNode*> fnodes[2];
10078     int iLinkNode[2][2];
10079     TIDSortedElemSet avoidSet;
10080     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10081       const SMDS_MeshNode* n1 = link[iSide].first;
10082       const SMDS_MeshNode* n2 = link[iSide].second;
10083       //cout << "Side " << iSide << " ";
10084       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10085       // find a face by two link nodes
10086       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10087                                                       *faceSetPtr[ iSide ], avoidSet,
10088                                                       &iLinkNode[iSide][0],
10089                                                       &iLinkNode[iSide][1] );
10090       if ( face[ iSide ])
10091       {
10092         //cout << " F " << face[ iSide]->GetID() <<endl;
10093         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10094         // put face nodes to fnodes
10095         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10096         fnodes[ iSide ].assign( nIt, nEnd );
10097         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10098       }
10099     }
10100
10101     // check similarity of elements of the sides
10102     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10103       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10104       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10105         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10106       }
10107       else {
10108         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10109       }
10110       break; // do not return because it's necessary to remove tmp faces
10111     }
10112
10113     // set nodes to merge
10114     // -------------------
10115
10116     if ( face[0] && face[1] )  {
10117       const int nbNodes = face[0]->NbNodes();
10118       if ( nbNodes != face[1]->NbNodes() ) {
10119         MESSAGE("Diff nb of face nodes");
10120         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10121         break; // do not return because it s necessary to remove tmp faces
10122       }
10123       bool reverse[] = { false, false }; // order of nodes in the link
10124       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10125         // analyse link orientation in faces
10126         int i1 = iLinkNode[ iSide ][ 0 ];
10127         int i2 = iLinkNode[ iSide ][ 1 ];
10128         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10129       }
10130       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10131       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10132       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10133       {
10134         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10135                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10136       }
10137
10138       // add other links of the faces to linkList
10139       // -----------------------------------------
10140
10141       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10142         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10143         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10144         if ( !iter_isnew.second ) { // already in a set: no need to process
10145           linkIdSet.erase( iter_isnew.first );
10146         }
10147         else // new in set == encountered for the first time: add
10148         {
10149           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10150           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10151           linkList[0].push_back ( NLink( n1, n2 ));
10152           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10153         }
10154       }
10155     } // 2 faces found
10156
10157     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10158       break;
10159
10160   } // loop on link lists
10161
10162   if ( aResult == SEW_OK &&
10163        ( //linkIt[0] != linkList[0].end() ||
10164         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10165     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10166              " " << (faceSetPtr[1]->empty()));
10167     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10168   }
10169
10170   // ====================================================================
10171   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10172   // ====================================================================
10173
10174   // delete temporary faces
10175   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10176   //  while ( tmpFaceIt->more() )
10177   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10178   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10179   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10180     aMesh->RemoveElement(*tmpFaceIt);
10181
10182   if ( aResult != SEW_OK)
10183     return aResult;
10184
10185   list< smIdType > nodeIDsToRemove;
10186   vector< const SMDS_MeshNode*> nodes;
10187   ElemFeatures elemType;
10188
10189   // loop on nodes replacement map
10190   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10191   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10192     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10193     {
10194       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10195       nodeIDsToRemove.push_back( nToRemove->GetID() );
10196       // loop on elements sharing nToRemove
10197       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10198       while ( invElemIt->more() ) {
10199         const SMDS_MeshElement* e = invElemIt->next();
10200         // get a new suite of nodes: make replacement
10201         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10202         nodes.resize( nbNodes );
10203         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10204         while ( nIt->more() ) {
10205           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10206           nnIt = nReplaceMap.find( n );
10207           if ( nnIt != nReplaceMap.end() ) {
10208             nbReplaced++;
10209             n = (*nnIt).second;
10210           }
10211           nodes[ i++ ] = n;
10212         }
10213         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10214         //         elemIDsToRemove.push_back( e->GetID() );
10215         //       else
10216         if ( nbReplaced )
10217         {
10218           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10219           aMesh->RemoveElement( e );
10220
10221           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10222           {
10223             AddToSameGroups( newElem, e, aMesh );
10224             if ( int aShapeId = e->getshapeId() )
10225               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10226           }
10227         }
10228       }
10229     }
10230
10231   Remove( nodeIDsToRemove, true );
10232
10233   return aResult;
10234 }
10235
10236 //================================================================================
10237 /*!
10238  * \brief Find corresponding nodes in two sets of faces
10239  * \param theSide1 - first face set
10240  * \param theSide2 - second first face
10241  * \param theFirstNode1 - a boundary node of set 1
10242  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10243  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10244  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10245  * \param nReplaceMap - output map of corresponding nodes
10246  * \return bool  - is a success or not
10247  */
10248 //================================================================================
10249
10250 #ifdef _DEBUG_
10251 //#define DEBUG_MATCHING_NODES
10252 #endif
10253
10254 SMESH_MeshEditor::Sew_Error
10255 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10256                                     set<const SMDS_MeshElement*>& theSide2,
10257                                     const SMDS_MeshNode*          theFirstNode1,
10258                                     const SMDS_MeshNode*          theFirstNode2,
10259                                     const SMDS_MeshNode*          theSecondNode1,
10260                                     const SMDS_MeshNode*          theSecondNode2,
10261                                     TNodeNodeMap &                nReplaceMap)
10262 {
10263   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10264
10265   nReplaceMap.clear();
10266   //if ( theFirstNode1 != theFirstNode2 )
10267   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10268   //if ( theSecondNode1 != theSecondNode2 )
10269   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10270
10271   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10272   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10273
10274   list< NLink > linkList[2];
10275   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10276   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10277
10278   // loop on links in linkList; find faces by links and append links
10279   // of the found faces to linkList
10280   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10281   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10282     NLink link[] = { *linkIt[0], *linkIt[1] };
10283     if ( linkSet.find( link[0] ) == linkSet.end() )
10284       continue;
10285
10286     // by links, find faces in the face sets,
10287     // and find indices of link nodes in the found faces;
10288     // in a face set, there is only one or no face sharing a link
10289     // ---------------------------------------------------------------
10290
10291     const SMDS_MeshElement* face[] = { 0, 0 };
10292     list<const SMDS_MeshNode*> notLinkNodes[2];
10293     //bool reverse[] = { false, false }; // order of notLinkNodes
10294     int nbNodes[2];
10295     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10296     {
10297       const SMDS_MeshNode* n1 = link[iSide].first;
10298       const SMDS_MeshNode* n2 = link[iSide].second;
10299       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10300       set< const SMDS_MeshElement* > facesOfNode1;
10301       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10302       {
10303         // during a loop of the first node, we find all faces around n1,
10304         // during a loop of the second node, we find one face sharing both n1 and n2
10305         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10306         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10307         while ( fIt->more() ) { // loop on faces sharing a node
10308           const SMDS_MeshElement* f = fIt->next();
10309           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10310               ! facesOfNode1.insert( f ).second ) // f encounters twice
10311           {
10312             if ( face[ iSide ] ) {
10313               MESSAGE( "2 faces per link " );
10314               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10315             }
10316             face[ iSide ] = f;
10317             faceSet->erase( f );
10318
10319             // get not link nodes
10320             int nbN = f->NbNodes();
10321             if ( f->IsQuadratic() )
10322               nbN /= 2;
10323             nbNodes[ iSide ] = nbN;
10324             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10325             int i1 = f->GetNodeIndex( n1 );
10326             int i2 = f->GetNodeIndex( n2 );
10327             int iEnd = nbN, iBeg = -1, iDelta = 1;
10328             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10329             if ( reverse ) {
10330               std::swap( iEnd, iBeg ); iDelta = -1;
10331             }
10332             int i = i2;
10333             while ( true ) {
10334               i += iDelta;
10335               if ( i == iEnd ) i = iBeg + iDelta;
10336               if ( i == i1 ) break;
10337               nodes.push_back ( f->GetNode( i ) );
10338             }
10339           }
10340         }
10341       }
10342     }
10343     // check similarity of elements of the sides
10344     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10345       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10346       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10347         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10348       }
10349       else {
10350         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10351       }
10352     }
10353
10354     // set nodes to merge
10355     // -------------------
10356
10357     if ( face[0] && face[1] )  {
10358       if ( nbNodes[0] != nbNodes[1] ) {
10359         MESSAGE("Diff nb of face nodes");
10360         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10361       }
10362 #ifdef DEBUG_MATCHING_NODES
10363       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10364                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10365                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10366 #endif
10367       int nbN = nbNodes[0];
10368       {
10369         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10370         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10371         for ( int i = 0 ; i < nbN - 2; ++i ) {
10372 #ifdef DEBUG_MATCHING_NODES
10373           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10374 #endif
10375           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10376         }
10377       }
10378
10379       // add other links of the face 1 to linkList
10380       // -----------------------------------------
10381
10382       const SMDS_MeshElement* f0 = face[0];
10383       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10384       for ( int i = 0; i < nbN; i++ )
10385       {
10386         const SMDS_MeshNode* n2 = f0->GetNode( i );
10387         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10388           linkSet.insert( SMESH_TLink( n1, n2 ));
10389         if ( !iter_isnew.second ) { // already in a set: no need to process
10390           linkSet.erase( iter_isnew.first );
10391         }
10392         else // new in set == encountered for the first time: add
10393         {
10394 #ifdef DEBUG_MATCHING_NODES
10395           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10396                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10397 #endif
10398           linkList[0].push_back ( NLink( n1, n2 ));
10399           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10400         }
10401         n1 = n2;
10402       }
10403     } // 2 faces found
10404   } // loop on link lists
10405
10406   return SEW_OK;
10407 }
10408
10409 namespace // automatically find theAffectedElems for DoubleNodes()
10410 {
10411   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10412
10413   //--------------------------------------------------------------------------------
10414   // Nodes shared by adjacent FissureBorder's.
10415   // 1 node  if FissureBorder separates faces
10416   // 2 nodes if FissureBorder separates volumes
10417   struct SubBorder
10418   {
10419     const SMDS_MeshNode* _nodes[2];
10420     int                  _nbNodes;
10421
10422     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10423     {
10424       _nodes[0] = n1;
10425       _nodes[1] = n2;
10426       _nbNodes = bool( n1 ) + bool( n2 );
10427       if ( _nbNodes == 2 && n1 > n2 )
10428         std::swap( _nodes[0], _nodes[1] );
10429     }
10430     bool operator<( const SubBorder& other ) const
10431     {
10432       for ( int i = 0; i < _nbNodes; ++i )
10433       {
10434         if ( _nodes[i] < other._nodes[i] ) return true;
10435         if ( _nodes[i] > other._nodes[i] ) return false;
10436       }
10437       return false;
10438     }
10439   };
10440
10441   //--------------------------------------------------------------------------------
10442   // Map a SubBorder to all FissureBorder it bounds
10443   struct FissureBorder;
10444   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10445   typedef TBorderLinks::iterator                               TMappedSub;
10446
10447   //--------------------------------------------------------------------------------
10448   /*!
10449    * \brief Element border (volume facet or face edge) at a fissure
10450    */
10451   struct FissureBorder
10452   {
10453     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10454     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10455
10456     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10457     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10458
10459     FissureBorder( FissureBorder && from ) // move constructor
10460     {
10461       std::swap( _nodes,       from._nodes );
10462       std::swap( _sortedNodes, from._sortedNodes );
10463       _elems[0] = from._elems[0];
10464       _elems[1] = from._elems[1];
10465     }
10466
10467     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10468                    std::vector< const SMDS_MeshElement* > & adjElems)
10469       : _nodes( elemToDuplicate->NbCornerNodes() )
10470     {
10471       for ( size_t i = 0; i < _nodes.size(); ++i )
10472         _nodes[i] = elemToDuplicate->GetNode( i );
10473
10474       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10475       findAdjacent( type, adjElems );
10476     }
10477
10478     FissureBorder( const SMDS_MeshNode**                    nodes,
10479                    const size_t                             nbNodes,
10480                    const SMDSAbs_ElementType                adjElemsType,
10481                    std::vector< const SMDS_MeshElement* > & adjElems)
10482       : _nodes( nodes, nodes + nbNodes )
10483     {
10484       findAdjacent( adjElemsType, adjElems );
10485     }
10486
10487     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10488                        std::vector< const SMDS_MeshElement* > & adjElems)
10489     {
10490       _elems[0] = _elems[1] = 0;
10491       adjElems.clear();
10492       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10493         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10494           _elems[i] = adjElems[i];
10495     }
10496
10497     bool operator<( const FissureBorder& other ) const
10498     {
10499       return GetSortedNodes() < other.GetSortedNodes();
10500     }
10501
10502     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10503     {
10504       if ( _sortedNodes.empty() && !_nodes.empty() )
10505       {
10506         FissureBorder* me = const_cast<FissureBorder*>( this );
10507         me->_sortedNodes = me->_nodes;
10508         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10509       }
10510       return _sortedNodes;
10511     }
10512
10513     size_t NbSub() const
10514     {
10515       return _nodes.size();
10516     }
10517
10518     SubBorder Sub(size_t i) const
10519     {
10520       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10521     }
10522
10523     void AddSelfTo( TBorderLinks& borderLinks )
10524     {
10525       _mappedSubs.resize( NbSub() );
10526       for ( size_t i = 0; i < NbSub(); ++i )
10527       {
10528         TBorderLinks::iterator s2b =
10529           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10530         s2b->second.push_back( this );
10531         _mappedSubs[ i ] = s2b;
10532       }
10533     }
10534
10535     void Clear()
10536     {
10537       _nodes.clear();
10538     }
10539
10540     const SMDS_MeshElement* GetMarkedElem() const
10541     {
10542       if ( _nodes.empty() ) return 0; // cleared
10543       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10544       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10545       return 0;
10546     }
10547
10548     gp_XYZ GetNorm() const // normal to the border
10549     {
10550       gp_XYZ norm;
10551       if ( _nodes.size() == 2 )
10552       {
10553         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10554         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10555           avgNorm += norm;
10556         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10557           avgNorm += norm;
10558
10559         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10560         norm = bordDir ^ avgNorm;
10561       }
10562       else
10563       {
10564         SMESH_NodeXYZ p0( _nodes[0] );
10565         SMESH_NodeXYZ p1( _nodes[1] );
10566         SMESH_NodeXYZ p2( _nodes[2] );
10567         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10568       }
10569       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10570         norm.Reverse();
10571
10572       return norm;
10573     }
10574
10575     void ChooseSide() // mark an _elem located at positive side of fissure
10576     {
10577       _elems[0]->setIsMarked( true );
10578       gp_XYZ norm = GetNorm();
10579       double maxX = norm.Coord(1);
10580       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10581       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10582       if ( maxX < 0 )
10583       {
10584         _elems[0]->setIsMarked( false );
10585         if ( _elems[1] )
10586           _elems[1]->setIsMarked( true );
10587       }
10588     }
10589
10590   }; // struct FissureBorder
10591
10592   //--------------------------------------------------------------------------------
10593   /*!
10594    * \brief Classifier of elements at fissure edge
10595    */
10596   class FissureNormal
10597   {
10598     std::vector< gp_XYZ > _normals;
10599     bool                  _bothIn;
10600
10601   public:
10602     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10603     {
10604       _bothIn = false;
10605       _normals.reserve(2);
10606       _normals.push_back( bord.GetNorm() );
10607       if ( _normals.size() == 2 )
10608         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10609     }
10610
10611     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10612     {
10613       bool isIn = false;
10614       switch ( _normals.size() ) {
10615       case 1:
10616       {
10617         isIn = !isOut( n, _normals[0], elem );
10618         break;
10619       }
10620       case 2:
10621       {
10622         bool in1 = !isOut( n, _normals[0], elem );
10623         bool in2 = !isOut( n, _normals[1], elem );
10624         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10625       }
10626       }
10627       return isIn;
10628     }
10629   };
10630
10631   //================================================================================
10632   /*!
10633    * \brief Classify an element by a plane passing through a node
10634    */
10635   //================================================================================
10636
10637   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10638   {
10639     SMESH_NodeXYZ p = n;
10640     double sumDot = 0;
10641     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10642     {
10643       SMESH_NodeXYZ pi = elem->GetNode( i );
10644       sumDot += norm * ( pi - p );
10645     }
10646     return sumDot < -1e-100;
10647   }
10648
10649   //================================================================================
10650   /*!
10651    * \brief Find FissureBorder's by nodes to duplicate
10652    */
10653   //================================================================================
10654
10655   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10656                            std::vector< FissureBorder > & theFissureBorders )
10657   {
10658     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10659     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10660     if ( !n ) return;
10661     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10662     if ( n->NbInverseElements( elemType ) == 0 )
10663     {
10664       elemType = SMDSAbs_Face;
10665       if ( n->NbInverseElements( elemType ) == 0 )
10666         return;
10667     }
10668     // unmark elements touching the fissure
10669     for ( ; nIt != theNodes.end(); ++nIt )
10670       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10671
10672     // loop on elements touching the fissure to get their borders belonging to the fissure
10673     std::set< FissureBorder >              fissureBorders;
10674     std::vector< const SMDS_MeshElement* > adjElems;
10675     std::vector< const SMDS_MeshNode* >    nodes;
10676     SMDS_VolumeTool volTool;
10677     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10678     {
10679       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10680       while ( invIt->more() )
10681       {
10682         const SMDS_MeshElement* eInv = invIt->next();
10683         if ( eInv->isMarked() ) continue;
10684         eInv->setIsMarked( true );
10685
10686         if ( elemType == SMDSAbs_Volume )
10687         {
10688           volTool.Set( eInv );
10689           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10690           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10691           {
10692             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10693             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10694             nodes.clear();
10695             bool allOnFissure = true;
10696             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10697               if (( allOnFissure = theNodes.count( nn[ iN ])))
10698                 nodes.push_back( nn[ iN ]);
10699             if ( allOnFissure )
10700               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10701                                                                elemType, adjElems )));
10702           }
10703         }
10704         else // elemType == SMDSAbs_Face
10705         {
10706           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10707           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10708           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10709           {
10710             nn[1]      = eInv->GetNode( iN );
10711             onFissure1 = theNodes.count( nn[1] );
10712             if ( onFissure0 && onFissure1 )
10713               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10714             nn[0]      = nn[1];
10715             onFissure0 = onFissure1;
10716           }
10717         }
10718       }
10719     }
10720
10721     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10722     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10723     for ( ; bord != fissureBorders.end(); ++bord )
10724     {
10725       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10726     }
10727     return;
10728   } // findFissureBorders()
10729
10730   //================================================================================
10731   /*!
10732    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10733    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10734    *  \param [in] theNodesNot - nodes not to duplicate
10735    *  \param [out] theAffectedElems - the found elements
10736    */
10737   //================================================================================
10738
10739   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10740                           TIDSortedElemSet&       theAffectedElems)
10741   {
10742     if ( theElemsOrNodes.empty() ) return;
10743
10744     // find FissureBorder's
10745
10746     std::vector< FissureBorder >           fissure;
10747     std::vector< const SMDS_MeshElement* > elemsByFacet;
10748
10749     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10750     if ( (*elIt)->GetType() == SMDSAbs_Node )
10751     {
10752       findFissureBorders( theElemsOrNodes, fissure );
10753     }
10754     else
10755     {
10756       fissure.reserve( theElemsOrNodes.size() );
10757       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10758       {
10759         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10760         if ( !fissure.back()._elems[1] )
10761           fissure.pop_back();
10762       }
10763     }
10764     if ( fissure.empty() )
10765       return;
10766
10767     // fill borderLinks
10768
10769     TBorderLinks borderLinks;
10770
10771     for ( size_t i = 0; i < fissure.size(); ++i )
10772     {
10773       fissure[i].AddSelfTo( borderLinks );
10774     }
10775
10776     // get theAffectedElems
10777
10778     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10779     for ( size_t i = 0; i < fissure.size(); ++i )
10780       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10781       {
10782         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10783                                         false, /*markElem=*/true );
10784       }
10785
10786     std::vector<const SMDS_MeshNode *>                 facetNodes;
10787     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10788     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10789
10790     // choose a side of fissure
10791     fissure[0].ChooseSide();
10792     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10793
10794     size_t nbCheckedBorders = 0;
10795     while ( nbCheckedBorders < fissure.size() )
10796     {
10797       // find a FissureBorder to treat
10798       FissureBorder* bord = 0;
10799       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10800         if ( fissure[i].GetMarkedElem() )
10801           bord = & fissure[i];
10802       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10803         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10804         {
10805           bord = & fissure[i];
10806           bord->ChooseSide();
10807           theAffectedElems.insert( bord->GetMarkedElem() );
10808         }
10809       if ( !bord ) return;
10810       ++nbCheckedBorders;
10811
10812       // treat FissureBorder's linked to bord
10813       fissureNodes.clear();
10814       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10815       for ( size_t i = 0; i < bord->NbSub(); ++i )
10816       {
10817         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10818         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10819         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10820         const SubBorder&                          sb = l2b->first;
10821         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10822
10823         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10824         {
10825           for ( int j = 0; j < sb._nbNodes; ++j )
10826             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10827           continue;
10828         }
10829
10830         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10831         // until an elem adjacent to a neighbour FissureBorder is found
10832         facetNodes.clear();
10833         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10834         facetNodes.resize( sb._nbNodes + 1 );
10835
10836         while ( bordElem )
10837         {
10838           // check if bordElem is adjacent to a neighbour FissureBorder
10839           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10840           {
10841             FissureBorder* bord2 = linkedBorders[j];
10842             if ( bord2 == bord ) continue;
10843             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10844               bordElem = 0;
10845             else
10846               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10847           }
10848           if ( !bordElem )
10849             break;
10850
10851           // find the next bordElem
10852           const SMDS_MeshElement* nextBordElem = 0;
10853           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10854           {
10855             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10856             if ( fissureNodes.count( n )) continue;
10857
10858             facetNodes[ sb._nbNodes ] = n;
10859             elemsByFacet.clear();
10860             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10861             {
10862               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10863                 if ( elemsByFacet[ iE ] != bordElem &&
10864                      !elemsByFacet[ iE ]->isMarked() )
10865                 {
10866                   theAffectedElems.insert( elemsByFacet[ iE ]);
10867                   elemsByFacet[ iE ]->setIsMarked( true );
10868                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10869                     nextBordElem = elemsByFacet[ iE ];
10870                 }
10871             }
10872           }
10873           bordElem = nextBordElem;
10874
10875         } // while ( bordElem )
10876
10877         linkedBorders.clear(); // not to treat this link any more
10878
10879       } // loop on SubBorder's of a FissureBorder
10880
10881       bord->Clear();
10882
10883     } // loop on FissureBorder's
10884
10885
10886     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10887
10888     // mark nodes of theAffectedElems
10889     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10890
10891     // unmark nodes of the fissure
10892     elIt = theElemsOrNodes.begin();
10893     if ( (*elIt)->GetType() == SMDSAbs_Node )
10894       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10895     else
10896       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10897
10898     std::vector< gp_XYZ > normVec;
10899
10900     // loop on nodes of the fissure, add elements having marked nodes
10901     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10902     {
10903       const SMDS_MeshElement* e = (*elIt);
10904       if ( e->GetType() != SMDSAbs_Node )
10905         e->setIsMarked( true ); // avoid adding a fissure element
10906
10907       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10908       {
10909         const SMDS_MeshNode* n = e->GetNode( iN );
10910         if ( fissEdgeNodes2Norm.count( n ))
10911           continue;
10912
10913         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10914         while ( invIt->more() )
10915         {
10916           const SMDS_MeshElement* eInv = invIt->next();
10917           if ( eInv->isMarked() ) continue;
10918           eInv->setIsMarked( true );
10919
10920           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10921           while( nIt->more() )
10922             if ( nIt->next()->isMarked())
10923             {
10924               theAffectedElems.insert( eInv );
10925               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10926               n->setIsMarked( false );
10927               break;
10928             }
10929         }
10930       }
10931     }
10932
10933     // add elements on the fissure edge
10934     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10935     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10936     {
10937       const SMDS_MeshNode* edgeNode = n2N->first;
10938       const FissureNormal & normals = n2N->second;
10939
10940       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10941       while ( invIt->more() )
10942       {
10943         const SMDS_MeshElement* eInv = invIt->next();
10944         if ( eInv->isMarked() ) continue;
10945         eInv->setIsMarked( true );
10946
10947         // classify eInv using normals
10948         bool toAdd = normals.IsIn( edgeNode, eInv );
10949         if ( toAdd ) // check if all nodes lie on the fissure edge
10950         {
10951           bool notOnEdge = false;
10952           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10953             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10954           toAdd = notOnEdge;
10955         }
10956         if ( toAdd )
10957         {
10958           theAffectedElems.insert( eInv );
10959         }
10960       }
10961     }
10962
10963     return;
10964   } // findAffectedElems()
10965 } // namespace
10966
10967 //================================================================================
10968 /*!
10969  * \brief Create elements equal (on same nodes) to given ones
10970  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10971  *              elements of the uppest dimension are duplicated.
10972  */
10973 //================================================================================
10974
10975 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10976 {
10977   ClearLastCreated();
10978   SMESHDS_Mesh* mesh = GetMeshDS();
10979
10980   // get an element type and an iterator over elements
10981
10982   SMDSAbs_ElementType type = SMDSAbs_All;
10983   SMDS_ElemIteratorPtr elemIt;
10984   if ( theElements.empty() )
10985   {
10986     if ( mesh->NbNodes() == 0 )
10987       return;
10988     // get most complex type
10989     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10990       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10991       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10992     };
10993     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10994       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10995       {
10996         type = types[i];
10997         elemIt = mesh->elementsIterator( type );
10998         break;
10999       }
11000   }
11001   else
11002   {
11003     //type = (*theElements.begin())->GetType();
11004     elemIt = SMESHUtils::elemSetIterator( theElements );
11005   }
11006
11007   // un-mark all elements to avoid duplicating just created elements
11008   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11009
11010   // duplicate elements
11011
11012   ElemFeatures elemType;
11013
11014   vector< const SMDS_MeshNode* > nodes;
11015   while ( elemIt->more() )
11016   {
11017     const SMDS_MeshElement* elem = elemIt->next();
11018     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11019         ( elem->isMarked() ))
11020       continue;
11021
11022     elemType.Init( elem, /*basicOnly=*/false );
11023     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11024
11025     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11026       newElem->setIsMarked( true );
11027   }
11028 }
11029
11030 //================================================================================
11031 /*!
11032   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11033   \param theElems - the list of elements (edges or faces) to be replicated
11034   The nodes for duplication could be found from these elements
11035   \param theNodesNot - list of nodes to NOT replicate
11036   \param theAffectedElems - the list of elements (cells and edges) to which the
11037   replicated nodes should be associated to.
11038   \return TRUE if operation has been completed successfully, FALSE otherwise
11039 */
11040 //================================================================================
11041
11042 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11043                                     const TIDSortedElemSet& theNodesNot,
11044                                     const TIDSortedElemSet& theAffectedElems )
11045 {
11046   ClearLastCreated();
11047
11048   if ( theElems.size() == 0 )
11049     return false;
11050
11051   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11052   if ( !aMeshDS )
11053     return false;
11054
11055   bool res = false;
11056   TNodeNodeMap anOldNodeToNewNode;
11057   // duplicate elements and nodes
11058   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11059   // replce nodes by duplications
11060   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11061   return res;
11062 }
11063
11064 //================================================================================
11065 /*!
11066   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11067   \param theMeshDS - mesh instance
11068   \param theElems - the elements replicated or modified (nodes should be changed)
11069   \param theNodesNot - nodes to NOT replicate
11070   \param theNodeNodeMap - relation of old node to new created node
11071   \param theIsDoubleElem - flag os to replicate element or modify
11072   \return TRUE if operation has been completed successfully, FALSE otherwise
11073 */
11074 //================================================================================
11075
11076 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11077                                    const TIDSortedElemSet& theElems,
11078                                    const TIDSortedElemSet& theNodesNot,
11079                                    TNodeNodeMap&           theNodeNodeMap,
11080                                    const bool              theIsDoubleElem )
11081 {
11082   // iterate through element and duplicate them (by nodes duplication)
11083   bool res = false;
11084   std::vector<const SMDS_MeshNode*> newNodes;
11085   ElemFeatures elemType;
11086
11087   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11088   for ( ;  elemItr != theElems.end(); ++elemItr )
11089   {
11090     const SMDS_MeshElement* anElem = *elemItr;
11091     // if (!anElem)
11092     //   continue;
11093
11094     // duplicate nodes to duplicate element
11095     bool isDuplicate = false;
11096     newNodes.resize( anElem->NbNodes() );
11097     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11098     int ind = 0;
11099     while ( anIter->more() )
11100     {
11101       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11102       const SMDS_MeshNode*  aNewNode = aCurrNode;
11103       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11104       if ( n2n != theNodeNodeMap.end() )
11105       {
11106         aNewNode = n2n->second;
11107       }
11108       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11109       {
11110         // duplicate node
11111         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11112         copyPosition( aCurrNode, aNewNode );
11113         theNodeNodeMap[ aCurrNode ] = aNewNode;
11114         myLastCreatedNodes.push_back( aNewNode );
11115       }
11116       isDuplicate |= (aCurrNode != aNewNode);
11117       newNodes[ ind++ ] = aNewNode;
11118     }
11119     if ( !isDuplicate )
11120       continue;
11121
11122     if ( theIsDoubleElem )
11123       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11124     else
11125     {
11126       // change element nodes
11127       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11128       if ( geomType == SMDSEntity_Polyhedra )
11129       {
11130         // special treatment for polyhedron
11131         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11132         if (!aPolyhedron) {
11133           MESSAGE("Warning: bad volumic element");
11134           return false;
11135         }
11136         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11137       }
11138       else
11139         // standard entity type
11140         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11141     }
11142
11143     res = true;
11144   }
11145   return res;
11146 }
11147
11148 //================================================================================
11149 /*!
11150   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11151   \param theNodes - identifiers of nodes to be doubled
11152   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11153   nodes. If list of element identifiers is empty then nodes are doubled but
11154   they not assigned to elements
11155   \return TRUE if operation has been completed successfully, FALSE otherwise
11156 */
11157 //================================================================================
11158
11159 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11160                                     const std::list< int >& theListOfModifiedElems )
11161 {
11162   ClearLastCreated();
11163
11164   if ( theListOfNodes.size() == 0 )
11165     return false;
11166
11167   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11168   if ( !aMeshDS )
11169     return false;
11170
11171   // iterate through nodes and duplicate them
11172
11173   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11174
11175   std::list< int >::const_iterator aNodeIter;
11176   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11177   {
11178     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11179     if ( !aNode )
11180       continue;
11181
11182     // duplicate node
11183
11184     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11185     if ( aNewNode )
11186     {
11187       copyPosition( aNode, aNewNode );
11188       anOldNodeToNewNode[ aNode ] = aNewNode;
11189       myLastCreatedNodes.push_back( aNewNode );
11190     }
11191   }
11192
11193   // Change nodes of elements
11194
11195   std::vector<const SMDS_MeshNode*> aNodeArr;
11196
11197   std::list< int >::const_iterator anElemIter;
11198   for ( anElemIter =  theListOfModifiedElems.begin();
11199         anElemIter != theListOfModifiedElems.end();
11200         anElemIter++ )
11201   {
11202     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11203     if ( !anElem )
11204       continue;
11205
11206     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11207     for( size_t i = 0; i < aNodeArr.size(); ++i )
11208     {
11209       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11210         anOldNodeToNewNode.find( aNodeArr[ i ]);
11211       if ( n2n != anOldNodeToNewNode.end() )
11212         aNodeArr[ i ] = n2n->second;
11213     }
11214     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11215   }
11216
11217   return true;
11218 }
11219
11220 namespace {
11221
11222   //================================================================================
11223   /*!
11224     \brief Check if element located inside shape
11225     \return TRUE if IN or ON shape, FALSE otherwise
11226   */
11227   //================================================================================
11228
11229   template<class Classifier>
11230   bool isInside(const SMDS_MeshElement* theElem,
11231                 Classifier&             theClassifier,
11232                 const double            theTol)
11233   {
11234     gp_XYZ centerXYZ (0, 0, 0);
11235     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11236       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11237
11238     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11239     theClassifier.Perform(aPnt, theTol);
11240     TopAbs_State aState = theClassifier.State();
11241     return (aState == TopAbs_IN || aState == TopAbs_ON );
11242   }
11243
11244   //================================================================================
11245   /*!
11246    * \brief Classifier of the 3D point on the TopoDS_Face
11247    *        with interaface suitable for isInside()
11248    */
11249   //================================================================================
11250
11251   struct _FaceClassifier
11252   {
11253     Extrema_ExtPS       _extremum;
11254     BRepAdaptor_Surface _surface;
11255     TopAbs_State        _state;
11256
11257     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11258     {
11259       _extremum.Initialize( _surface,
11260                             _surface.FirstUParameter(), _surface.LastUParameter(),
11261                             _surface.FirstVParameter(), _surface.LastVParameter(),
11262                             _surface.Tolerance(), _surface.Tolerance() );
11263     }
11264     void Perform(const gp_Pnt& aPnt, double theTol)
11265     {
11266       theTol *= theTol;
11267       _state = TopAbs_OUT;
11268       _extremum.Perform(aPnt);
11269       if ( _extremum.IsDone() )
11270         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11271           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11272     }
11273     TopAbs_State State() const
11274     {
11275       return _state;
11276     }
11277   };
11278 }
11279
11280 //================================================================================
11281 /*!
11282   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11283   This method is the first step of DoubleNodeElemGroupsInRegion.
11284   \param theElems - list of groups of elements (edges or faces) to be replicated
11285   \param theNodesNot - list of groups of nodes not to replicate
11286   \param theShape - shape to detect affected elements (element which geometric center
11287          located on or inside shape). If the shape is null, detection is done on faces orientations
11288          (select elements with a gravity center on the side given by faces normals).
11289          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11290          The replicated nodes should be associated to affected elements.
11291   \return true
11292   \sa DoubleNodeElemGroupsInRegion()
11293 */
11294 //================================================================================
11295
11296 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11297                                                    const TIDSortedElemSet& theNodesNot,
11298                                                    const TopoDS_Shape&     theShape,
11299                                                    TIDSortedElemSet&       theAffectedElems)
11300 {
11301   if ( theShape.IsNull() )
11302   {
11303     findAffectedElems( theElems, theAffectedElems );
11304   }
11305   else
11306   {
11307     const double aTol = Precision::Confusion();
11308     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11309     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11310     if ( theShape.ShapeType() == TopAbs_SOLID )
11311     {
11312       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11313       bsc3d->PerformInfinitePoint(aTol);
11314     }
11315     else if (theShape.ShapeType() == TopAbs_FACE )
11316     {
11317       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11318     }
11319
11320     // iterates on indicated elements and get elements by back references from their nodes
11321     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11322     for ( ;  elemItr != theElems.end(); ++elemItr )
11323     {
11324       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11325       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11326       while ( nodeItr->more() )
11327       {
11328         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11329         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11330           continue;
11331         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11332         while ( backElemItr->more() )
11333         {
11334           const SMDS_MeshElement* curElem = backElemItr->next();
11335           if ( curElem && theElems.find(curElem) == theElems.end() &&
11336                ( bsc3d.get() ?
11337                  isInside( curElem, *bsc3d, aTol ) :
11338                  isInside( curElem, *aFaceClassifier, aTol )))
11339             theAffectedElems.insert( curElem );
11340         }
11341       }
11342     }
11343   }
11344   return true;
11345 }
11346
11347 //================================================================================
11348 /*!
11349   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11350   \param theElems - group of of elements (edges or faces) to be replicated
11351   \param theNodesNot - group of nodes not to replicate
11352   \param theShape - shape to detect affected elements (element which geometric center
11353   located on or inside shape).
11354   The replicated nodes should be associated to affected elements.
11355   \return TRUE if operation has been completed successfully, FALSE otherwise
11356 */
11357 //================================================================================
11358
11359 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11360                                             const TIDSortedElemSet& theNodesNot,
11361                                             const TopoDS_Shape&     theShape )
11362 {
11363   if ( theShape.IsNull() )
11364     return false;
11365
11366   const double aTol = Precision::Confusion();
11367   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11368   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11369   if ( theShape.ShapeType() == TopAbs_SOLID )
11370   {
11371     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11372     bsc3d->PerformInfinitePoint(aTol);
11373   }
11374   else if (theShape.ShapeType() == TopAbs_FACE )
11375   {
11376     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11377   }
11378
11379   // iterates on indicated elements and get elements by back references from their nodes
11380   TIDSortedElemSet anAffected;
11381   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11382   for ( ;  elemItr != theElems.end(); ++elemItr )
11383   {
11384     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11385     if (!anElem)
11386       continue;
11387
11388     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11389     while ( nodeItr->more() )
11390     {
11391       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11392       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11393         continue;
11394       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11395       while ( backElemItr->more() )
11396       {
11397         const SMDS_MeshElement* curElem = backElemItr->next();
11398         if ( curElem && theElems.find(curElem) == theElems.end() &&
11399              ( bsc3d ?
11400                isInside( curElem, *bsc3d, aTol ) :
11401                isInside( curElem, *aFaceClassifier, aTol )))
11402           anAffected.insert( curElem );
11403       }
11404     }
11405   }
11406   return DoubleNodes( theElems, theNodesNot, anAffected );
11407 }
11408
11409 /*!
11410  *  \brief compute an oriented angle between two planes defined by four points.
11411  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11412  *  @param p0 base of the rotation axe
11413  *  @param p1 extremity of the rotation axe
11414  *  @param g1 belongs to the first plane
11415  *  @param g2 belongs to the second plane
11416  */
11417 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11418 {
11419   gp_Vec vref(p0, p1);
11420   gp_Vec v1(p0, g1);
11421   gp_Vec v2(p0, g2);
11422   gp_Vec n1 = vref.Crossed(v1);
11423   gp_Vec n2 = vref.Crossed(v2);
11424   try {
11425     return n2.AngleWithRef(n1, vref);
11426   }
11427   catch ( Standard_Failure& ) {
11428   }
11429   return Max( v1.Magnitude(), v2.Magnitude() );
11430 }
11431
11432 /*!
11433  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11434  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11435  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11436  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11437  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11438  * 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.
11439  * 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.
11440  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11441  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11442  * \param theElems - list of groups of volumes, where a group of volume is a set of
11443  *        SMDS_MeshElements sorted by Id.
11444  * \param createJointElems - if TRUE, create the elements
11445  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11446  *        the boundary between \a theDomains and the rest mesh
11447  * \return TRUE if operation has been completed successfully, FALSE otherwise
11448  */
11449 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11450                                                      bool                                 createJointElems,
11451                                                      bool                                 onAllBoundaries)
11452 {
11453   // MESSAGE("----------------------------------------------");
11454   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11455   // MESSAGE("----------------------------------------------");
11456
11457   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11458   meshDS->BuildDownWardConnectivity(true);
11459   CHRONO(50);
11460   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11461
11462   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11463   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11464   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11465
11466   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11467   std::map<int,int> celldom; // cell vtkId --> domain
11468   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11469   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11470
11471   //MESSAGE(".. Number of domains :"<<theElems.size());
11472
11473   TIDSortedElemSet theRestDomElems;
11474   const int iRestDom  = -1;
11475   const int idom0     = onAllBoundaries ? iRestDom : 0;
11476   const int nbDomains = theElems.size();
11477
11478   // Check if the domains do not share an element
11479   for (int idom = 0; idom < nbDomains-1; idom++)
11480   {
11481     //       MESSAGE("... Check of domain #" << idom);
11482     const TIDSortedElemSet& domain = theElems[idom];
11483     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11484     for (; elemItr != domain.end(); ++elemItr)
11485     {
11486       const SMDS_MeshElement* anElem = *elemItr;
11487       int idombisdeb = idom + 1 ;
11488       // check if the element belongs to a domain further in the list
11489       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11490       {
11491         const TIDSortedElemSet& domainbis = theElems[idombis];
11492         if ( domainbis.count( anElem ))
11493         {
11494           MESSAGE(".... Domain #" << idom);
11495           MESSAGE(".... Domain #" << idombis);
11496           throw SALOME_Exception("The domains are not disjoint.");
11497           return false ;
11498         }
11499       }
11500     }
11501   }
11502
11503   for (int idom = 0; idom < nbDomains; idom++)
11504   {
11505
11506     // --- build a map (face to duplicate --> volume to modify)
11507     //     with all the faces shared by 2 domains (group of elements)
11508     //     and corresponding volume of this domain, for each shared face.
11509     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11510
11511     //MESSAGE("... Neighbors of domain #" << idom);
11512     const TIDSortedElemSet& domain = theElems[idom];
11513     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11514     for (; elemItr != domain.end(); ++elemItr)
11515     {
11516       const SMDS_MeshElement* anElem = *elemItr;
11517       if (!anElem)
11518         continue;
11519       vtkIdType vtkId = anElem->GetVtkID();
11520       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11521       int neighborsVtkIds[NBMAXNEIGHBORS];
11522       int downIds[NBMAXNEIGHBORS];
11523       unsigned char downTypes[NBMAXNEIGHBORS];
11524       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11525       for (int n = 0; n < nbNeighbors; n++)
11526       {
11527         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11528         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11529         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11530         {
11531           bool ok = false;
11532           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11533           {
11534             // MESSAGE("Domain " << idombis);
11535             const TIDSortedElemSet& domainbis = theElems[idombis];
11536             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11537           }
11538           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11539           {
11540             DownIdType face(downIds[n], downTypes[n]);
11541             if (!faceDomains[face].count(idom))
11542             {
11543               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11544               celldom[vtkId] = idom;
11545               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11546             }
11547             if ( !ok )
11548             {
11549               theRestDomElems.insert( elem );
11550               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11551               celldom[neighborsVtkIds[n]] = iRestDom;
11552             }
11553           }
11554         }
11555       }
11556     }
11557   }
11558
11559   //MESSAGE("Number of shared faces " << faceDomains.size());
11560   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11561
11562   // --- explore the shared faces domain by domain,
11563   //     explore the nodes of the face and see if they belong to a cell in the domain,
11564   //     which has only a node or an edge on the border (not a shared face)
11565
11566   for (int idomain = idom0; idomain < nbDomains; idomain++)
11567   {
11568     //MESSAGE("Domain " << idomain);
11569     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11570     itface = faceDomains.begin();
11571     for (; itface != faceDomains.end(); ++itface)
11572     {
11573       const std::map<int, int>& domvol = itface->second;
11574       if (!domvol.count(idomain))
11575         continue;
11576       DownIdType face = itface->first;
11577       //MESSAGE(" --- face " << face.cellId);
11578       std::set<int> oldNodes;
11579       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11580       std::set<int>::iterator itn = oldNodes.begin();
11581       for (; itn != oldNodes.end(); ++itn)
11582       {
11583         int oldId = *itn;
11584         //MESSAGE("     node " << oldId);
11585         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11586         for (int i=0; i<l.ncells; i++)
11587         {
11588           int vtkId = l.cells[i];
11589           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11590           if (!domain.count(anElem))
11591             continue;
11592           int vtkType = grid->GetCellType(vtkId);
11593           int downId = grid->CellIdToDownId(vtkId);
11594           if (downId < 0)
11595           {
11596             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11597             continue; // not OK at this stage of the algorithm:
11598             //no cells created after BuildDownWardConnectivity
11599           }
11600           DownIdType aCell(downId, vtkType);
11601           cellDomains[aCell][idomain] = vtkId;
11602           celldom[vtkId] = idomain;
11603           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11604         }
11605       }
11606     }
11607   }
11608
11609   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11610   //     for each shared face, get the nodes
11611   //     for each node, for each domain of the face, create a clone of the node
11612
11613   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11614   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11615   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11616
11617   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11618   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11619   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11620
11621   //MESSAGE(".. Duplication of the nodes");
11622   for (int idomain = idom0; idomain < nbDomains; idomain++)
11623   {
11624     itface = faceDomains.begin();
11625     for (; itface != faceDomains.end(); ++itface)
11626     {
11627       const std::map<int, int>& domvol = itface->second;
11628       if (!domvol.count(idomain))
11629         continue;
11630       DownIdType face = itface->first;
11631       //MESSAGE(" --- face " << face.cellId);
11632       std::set<int> oldNodes;
11633       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11634       std::set<int>::iterator itn = oldNodes.begin();
11635       for (; itn != oldNodes.end(); ++itn)
11636       {
11637         int oldId = *itn;
11638         if (nodeDomains[oldId].empty())
11639         {
11640           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11641           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11642         }
11643         std::map<int, int>::const_iterator itdom = domvol.begin();
11644         for (; itdom != domvol.end(); ++itdom)
11645         {
11646           int idom = itdom->first;
11647           //MESSAGE("         domain " << idom);
11648           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11649           {
11650             if (nodeDomains[oldId].size() >= 2) // a multiple node
11651             {
11652               vector<int> orderedDoms;
11653               //MESSAGE("multiple node " << oldId);
11654               if (mutipleNodes.count(oldId))
11655                 orderedDoms = mutipleNodes[oldId];
11656               else
11657               {
11658                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11659                 for (; it != nodeDomains[oldId].end(); ++it)
11660                   orderedDoms.push_back(it->first);
11661               }
11662               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11663               //stringstream txt;
11664               //for (int i=0; i<orderedDoms.size(); i++)
11665               //  txt << orderedDoms[i] << " ";
11666               //MESSAGE("orderedDoms " << txt.str());
11667               mutipleNodes[oldId] = orderedDoms;
11668             }
11669             double *coords = grid->GetPoint(oldId);
11670             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11671             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11672             int newId = newNode->GetVtkID();
11673             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11674             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11675           }
11676         }
11677       }
11678     }
11679   }
11680
11681   //MESSAGE(".. Creation of elements");
11682   for (int idomain = idom0; idomain < nbDomains; idomain++)
11683   {
11684     itface = faceDomains.begin();
11685     for (; itface != faceDomains.end(); ++itface)
11686     {
11687       std::map<int, int> domvol = itface->second;
11688       if (!domvol.count(idomain))
11689         continue;
11690       DownIdType face = itface->first;
11691       //MESSAGE(" --- face " << face.cellId);
11692       std::set<int> oldNodes;
11693       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11694       int nbMultipleNodes = 0;
11695       std::set<int>::iterator itn = oldNodes.begin();
11696       for (; itn != oldNodes.end(); ++itn)
11697       {
11698         int oldId = *itn;
11699         if (mutipleNodes.count(oldId))
11700           nbMultipleNodes++;
11701       }
11702       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11703       {
11704         //MESSAGE("multiple Nodes detected on a shared face");
11705         int downId = itface->first.cellId;
11706         unsigned char cellType = itface->first.cellType;
11707         // --- shared edge or shared face ?
11708         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11709         {
11710           int nodes[3];
11711           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11712           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11713             if (mutipleNodes.count(nodes[i]))
11714               if (!mutipleNodesToFace.count(nodes[i]))
11715                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11716         }
11717         else // shared face (between two volumes)
11718         {
11719           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11720           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11721           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11722           for (int ie =0; ie < nbEdges; ie++)
11723           {
11724             int nodes[3];
11725             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11726             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11727             {
11728               vector<int> vn0 = mutipleNodes[nodes[0]];
11729               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11730               vector<int> doms;
11731               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11732                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11733                   if ( vn0[i0] == vn1[i1] )
11734                     doms.push_back( vn0[ i0 ]);
11735               if ( doms.size() > 2 )
11736               {
11737                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11738                 double *coords = grid->GetPoint(nodes[0]);
11739                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11740                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11741                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11742                 gp_Pnt gref;
11743                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11744                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11745                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11746                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11747                 for ( size_t id = 0; id < doms.size(); id++ )
11748                 {
11749                   int idom = doms[id];
11750                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11751                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11752                   {
11753                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11754                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11755                     if (domain.count(elem))
11756                     {
11757                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11758                       domvol[idom] = (SMDS_MeshVolume*) svol;
11759                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11760                       double values[3] = { 0,0,0 };
11761                       vtkIdType npts = 0;
11762                       vtkIdType const *pts(nullptr);
11763                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11764                       for ( vtkIdType i = 0; i < npts; ++i )
11765                       {
11766                         double *coords = grid->GetPoint( pts[i] );
11767                         for ( int j = 0; j < 3; ++j )
11768                           values[j] += coords[j] / npts;
11769                       }
11770                       if ( id == 0 )
11771                       {
11772                         gref.SetCoord( values[0], values[1], values[2] );
11773                         angleDom[idom] = 0;
11774                       }
11775                       else
11776                       {
11777                         gp_Pnt g( values[0], values[1], values[2] );
11778                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11779                         //MESSAGE("  angle=" << angleDom[idom]);
11780                       }
11781                       break;
11782                     }
11783                   }
11784                 }
11785                 map<double, int> sortedDom; // sort domains by angle
11786                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11787                   sortedDom[ia->second] = ia->first;
11788                 vector<int> vnodes;
11789                 vector<int> vdom;
11790                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11791                 {
11792                   vdom.push_back(ib->second);
11793                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11794                 }
11795                 for (int ino = 0; ino < nbNodes; ino++)
11796                   vnodes.push_back(nodes[ino]);
11797                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11798               }
11799             }
11800           }
11801         }
11802       }
11803     }
11804   }
11805
11806   // --- iterate on shared faces (volumes to modify, face to extrude)
11807   //     get node id's of the face (id SMDS = id VTK)
11808   //     create flat element with old and new nodes if requested
11809
11810   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11811   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11812
11813   std::map<int, std::map<long,int> > nodeQuadDomains;
11814   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11815
11816   //MESSAGE(".. Creation of elements: simple junction");
11817   if ( createJointElems )
11818   {
11819     string joints2DName = "joints2D";
11820     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11821     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11822     string joints3DName = "joints3D";
11823     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11824     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11825
11826     itface = faceDomains.begin();
11827     for (; itface != faceDomains.end(); ++itface)
11828     {
11829       DownIdType face = itface->first;
11830       std::set<int> oldNodes;
11831       std::set<int>::iterator itn;
11832       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11833
11834       std::map<int, int>          domvol = itface->second;
11835       std::map<int, int>::iterator itdom = domvol.begin();
11836       int     dom1 = itdom->first;
11837       int vtkVolId = itdom->second;
11838       itdom++;
11839       int           dom2 = itdom->first;
11840       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11841                                                        nodeQuadDomains);
11842       stringstream grpname;
11843       grpname << "j_";
11844       if (dom1 < dom2)
11845         grpname << dom1 << "_" << dom2;
11846       else
11847         grpname << dom2 << "_" << dom1;
11848       string namegrp = grpname.str();
11849       if (!mapOfJunctionGroups.count(namegrp))
11850         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11851       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11852       if (sgrp)
11853         sgrp->Add(vol->GetID());
11854       if (vol->GetType() == SMDSAbs_Volume)
11855         joints3DGrp->Add(vol->GetID());
11856       else if (vol->GetType() == SMDSAbs_Face)
11857         joints2DGrp->Add(vol->GetID());
11858     }
11859   }
11860
11861   // --- create volumes on multiple domain intersection if requested
11862   //     iterate on mutipleNodesToFace
11863   //     iterate on edgesMultiDomains
11864
11865   //MESSAGE(".. Creation of elements: multiple junction");
11866   if (createJointElems)
11867   {
11868     // --- iterate on mutipleNodesToFace
11869
11870     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11871     for (; itn != mutipleNodesToFace.end(); ++itn)
11872     {
11873       int node = itn->first;
11874       vector<int> orderDom = itn->second;
11875       vector<vtkIdType> orderedNodes;
11876       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11877         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11878       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11879
11880       stringstream grpname;
11881       grpname << "m2j_";
11882       grpname << 0 << "_" << 0;
11883       string namegrp = grpname.str();
11884       if (!mapOfJunctionGroups.count(namegrp))
11885         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11886       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11887       if (sgrp)
11888         sgrp->Add(face->GetID());
11889     }
11890
11891     // --- iterate on edgesMultiDomains
11892
11893     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11894     for (; ite != edgesMultiDomains.end(); ++ite)
11895     {
11896       vector<int>    nodes = ite->first;
11897       vector<int> orderDom = ite->second;
11898       vector<vtkIdType> orderedNodes;
11899       if (nodes.size() == 2)
11900       {
11901         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11902         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11903           if ( orderDom.size() == 3 )
11904             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11905               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11906           else
11907             for (int idom = orderDom.size()-1; idom >=0; idom--)
11908               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11909         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11910
11911         string namegrp = "jointsMultiples";
11912         if (!mapOfJunctionGroups.count(namegrp))
11913           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11914         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11915         if (sgrp)
11916           sgrp->Add(vol->GetID());
11917       }
11918       else
11919       {
11920         //INFOS("Quadratic multiple joints not implemented");
11921         // TODO quadratic nodes
11922       }
11923     }
11924   }
11925
11926   // --- list the explicit faces and edges of the mesh that need to be modified,
11927   //     i.e. faces and edges built with one or more duplicated nodes.
11928   //     associate these faces or edges to their corresponding domain.
11929   //     only the first domain found is kept when a face or edge is shared
11930
11931   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11932   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11933
11934   //MESSAGE(".. Modification of elements");
11935   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11936   for (int idomain = idom0; idomain < nbDomains; idomain++)
11937   {
11938     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11939     for (; itnod != nodeDomains.end(); ++itnod)
11940     {
11941       int oldId = itnod->first;
11942       //MESSAGE("     node " << oldId);
11943       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11944       for (int i = 0; i < l.ncells; i++)
11945       {
11946         int vtkId = l.cells[i];
11947         int vtkType = grid->GetCellType(vtkId);
11948         int downId = grid->CellIdToDownId(vtkId);
11949         if (downId < 0)
11950           continue; // new cells: not to be modified
11951         DownIdType aCell(downId, vtkType);
11952         int volParents[1000];
11953         int nbvol = 0;
11954         nbvol = grid->GetParentVolumes(volParents, vtkId);
11955         if ( domainType == SMDSAbs_Volume )
11956         {
11957           nbvol = grid->GetParentVolumes(volParents, vtkId);
11958         }
11959         else // domainType == SMDSAbs_Face
11960         {
11961           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11962           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11963           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11964           for (int i=0; i< nbFaces; i++)
11965           {
11966             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11967             if (vtkFaceId >= 0)
11968               volParents[nbvol++] = vtkFaceId;
11969           }
11970         }
11971         for (int j = 0; j < nbvol; j++)
11972           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11973             if (!feDom.count(vtkId))
11974             {
11975               feDom[vtkId] = idomain;
11976               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11977               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11978               //        << " type " << vtkType << " downId " << downId);
11979             }
11980       }
11981     }
11982   }
11983
11984   // --- iterate on shared faces (volumes to modify, face to extrude)
11985   //     get node id's of the face
11986   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11987
11988   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11989   for (int m=0; m<3; m++)
11990   {
11991     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11992     itface = (*amap).begin();
11993     for (; itface != (*amap).end(); ++itface)
11994     {
11995       DownIdType face = itface->first;
11996       std::set<int> oldNodes;
11997       std::set<int>::iterator itn;
11998       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11999       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12000       std::map<int, int> localClonedNodeIds;
12001
12002       std::map<int, int> domvol = itface->second;
12003       std::map<int, int>::iterator itdom = domvol.begin();
12004       for (; itdom != domvol.end(); ++itdom)
12005       {
12006         int idom = itdom->first;
12007         int vtkVolId = itdom->second;
12008         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12009         localClonedNodeIds.clear();
12010         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12011         {
12012           int oldId = *itn;
12013           if (nodeDomains[oldId].count(idom))
12014           {
12015             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12016             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12017           }
12018         }
12019         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12020       }
12021     }
12022   }
12023
12024   // Remove empty groups (issue 0022812)
12025   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12026   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12027   {
12028     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12029       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12030   }
12031
12032   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12033   grid->DeleteLinks();
12034
12035   CHRONOSTOP(50);
12036   counters::stats();
12037   return true;
12038 }
12039
12040 /*!
12041  * \brief Double nodes on some external faces and create flat elements.
12042  * Flat elements are mainly used by some types of mechanic calculations.
12043  *
12044  * Each group of the list must be constituted of faces.
12045  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12046  * @param theElems - list of groups of faces, where a group of faces is a set of
12047  * SMDS_MeshElements sorted by Id.
12048  * @return TRUE if operation has been completed successfully, FALSE otherwise
12049  */
12050 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12051 {
12052   // MESSAGE("-------------------------------------------------");
12053   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12054   // MESSAGE("-------------------------------------------------");
12055
12056   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12057
12058   // --- For each group of faces
12059   //     duplicate the nodes, create a flat element based on the face
12060   //     replace the nodes of the faces by their clones
12061
12062   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12063   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12064   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12065
12066   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12067   {
12068     const TIDSortedElemSet&           domain = theElems[idom];
12069     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12070     for ( ; elemItr != domain.end(); ++elemItr )
12071     {
12072       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12073       if (!aFace)
12074         continue;
12075       // MESSAGE("aFace=" << aFace->GetID());
12076       bool isQuad = aFace->IsQuadratic();
12077       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12078
12079       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12080
12081       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12082       while (nodeIt->more())
12083       {
12084         const SMDS_MeshNode* node = nodeIt->next();
12085         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12086         if (isMedium)
12087           ln2.push_back(node);
12088         else
12089           ln0.push_back(node);
12090
12091         const SMDS_MeshNode* clone = 0;
12092         if (!clonedNodes.count(node))
12093         {
12094           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12095           copyPosition( node, clone );
12096           clonedNodes[node] = clone;
12097         }
12098         else
12099           clone = clonedNodes[node];
12100
12101         if (isMedium)
12102           ln3.push_back(clone);
12103         else
12104           ln1.push_back(clone);
12105
12106         const SMDS_MeshNode* inter = 0;
12107         if (isQuad && (!isMedium))
12108         {
12109           if (!intermediateNodes.count(node))
12110           {
12111             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12112             copyPosition( node, inter );
12113             intermediateNodes[node] = inter;
12114           }
12115           else
12116             inter = intermediateNodes[node];
12117           ln4.push_back(inter);
12118         }
12119       }
12120
12121       // --- extrude the face
12122
12123       vector<const SMDS_MeshNode*> ln;
12124       SMDS_MeshVolume* vol = 0;
12125       vtkIdType aType = aFace->GetVtkType();
12126       switch (aType)
12127       {
12128       case VTK_TRIANGLE:
12129         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12130         // MESSAGE("vol prism " << vol->GetID());
12131         ln.push_back(ln1[0]);
12132         ln.push_back(ln1[1]);
12133         ln.push_back(ln1[2]);
12134         break;
12135       case VTK_QUAD:
12136         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12137         // MESSAGE("vol hexa " << vol->GetID());
12138         ln.push_back(ln1[0]);
12139         ln.push_back(ln1[1]);
12140         ln.push_back(ln1[2]);
12141         ln.push_back(ln1[3]);
12142         break;
12143       case VTK_QUADRATIC_TRIANGLE:
12144         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12145                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12146         // MESSAGE("vol quad prism " << vol->GetID());
12147         ln.push_back(ln1[0]);
12148         ln.push_back(ln1[1]);
12149         ln.push_back(ln1[2]);
12150         ln.push_back(ln3[0]);
12151         ln.push_back(ln3[1]);
12152         ln.push_back(ln3[2]);
12153         break;
12154       case VTK_QUADRATIC_QUAD:
12155         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12156         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12157         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12158         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12159                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12160                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12161         // MESSAGE("vol quad hexa " << vol->GetID());
12162         ln.push_back(ln1[0]);
12163         ln.push_back(ln1[1]);
12164         ln.push_back(ln1[2]);
12165         ln.push_back(ln1[3]);
12166         ln.push_back(ln3[0]);
12167         ln.push_back(ln3[1]);
12168         ln.push_back(ln3[2]);
12169         ln.push_back(ln3[3]);
12170         break;
12171       case VTK_POLYGON:
12172         break;
12173       default:
12174         break;
12175       }
12176
12177       if (vol)
12178       {
12179         stringstream grpname;
12180         grpname << "jf_";
12181         grpname << idom;
12182         string namegrp = grpname.str();
12183         if (!mapOfJunctionGroups.count(namegrp))
12184           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12185         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12186         if (sgrp)
12187           sgrp->Add(vol->GetID());
12188       }
12189
12190       // --- modify the face
12191
12192       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12193     }
12194   }
12195   return true;
12196 }
12197
12198 /*!
12199  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12200  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12201  *  groups of faces to remove inside the object, (idem edges).
12202  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12203  */
12204 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12205                                       const TopoDS_Shape&             theShape,
12206                                       SMESH_NodeSearcher*             theNodeSearcher,
12207                                       const char*                     groupName,
12208                                       std::vector<double>&            nodesCoords,
12209                                       std::vector<std::vector<int> >& listOfListOfNodes)
12210 {
12211   // MESSAGE("--------------------------------");
12212   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12213   // MESSAGE("--------------------------------");
12214
12215   // --- zone of volumes to remove is given :
12216   //     1 either by a geom shape (one or more vertices) and a radius,
12217   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12218   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12219   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12220   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12221   //     defined by it's name.
12222
12223   SMESHDS_GroupBase* groupDS = 0;
12224   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12225   while ( groupIt->more() )
12226   {
12227     groupDS = 0;
12228     SMESH_Group * group = groupIt->next();
12229     if ( !group ) continue;
12230     groupDS = group->GetGroupDS();
12231     if ( !groupDS || groupDS->IsEmpty() ) continue;
12232     std::string grpName = group->GetName();
12233     //MESSAGE("grpName=" << grpName);
12234     if (grpName == groupName)
12235       break;
12236     else
12237       groupDS = 0;
12238   }
12239
12240   bool isNodeGroup = false;
12241   bool isNodeCoords = false;
12242   if (groupDS)
12243   {
12244     if (groupDS->GetType() != SMDSAbs_Node)
12245       return;
12246     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12247   }
12248
12249   if (nodesCoords.size() > 0)
12250     isNodeCoords = true; // a list o nodes given by their coordinates
12251   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12252
12253   // --- define groups to build
12254
12255   // --- group of SMDS volumes
12256   string grpvName = groupName;
12257   grpvName += "_vol";
12258   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12259   if (!grp)
12260   {
12261     MESSAGE("group not created " << grpvName);
12262     return;
12263   }
12264   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12265
12266   // --- group of SMDS faces on the skin
12267   string grpsName = groupName;
12268   grpsName += "_skin";
12269   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12270   if (!grps)
12271   {
12272     MESSAGE("group not created " << grpsName);
12273     return;
12274   }
12275   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12276
12277   // --- group of SMDS faces internal (several shapes)
12278   string grpiName = groupName;
12279   grpiName += "_internalFaces";
12280   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12281   if (!grpi)
12282   {
12283     MESSAGE("group not created " << grpiName);
12284     return;
12285   }
12286   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12287
12288   // --- group of SMDS faces internal (several shapes)
12289   string grpeiName = groupName;
12290   grpeiName += "_internalEdges";
12291   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12292   if (!grpei)
12293   {
12294     MESSAGE("group not created " << grpeiName);
12295     return;
12296   }
12297   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12298
12299   // --- build downward connectivity
12300
12301   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12302   meshDS->BuildDownWardConnectivity(true);
12303   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12304
12305   // --- set of volumes detected inside
12306
12307   std::set<int> setOfInsideVol;
12308   std::set<int> setOfVolToCheck;
12309
12310   std::vector<gp_Pnt> gpnts;
12311
12312   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12313   {
12314     //MESSAGE("group of nodes provided");
12315     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12316     while ( elemIt->more() )
12317     {
12318       const SMDS_MeshElement* elem = elemIt->next();
12319       if (!elem)
12320         continue;
12321       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12322       if (!node)
12323         continue;
12324       SMDS_MeshElement* vol = 0;
12325       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12326       while (volItr->more())
12327       {
12328         vol = (SMDS_MeshElement*)volItr->next();
12329         setOfInsideVol.insert(vol->GetVtkID());
12330         sgrp->Add(vol->GetID());
12331       }
12332     }
12333   }
12334   else if (isNodeCoords)
12335   {
12336     //MESSAGE("list of nodes coordinates provided");
12337     size_t i = 0;
12338     int k = 0;
12339     while ( i < nodesCoords.size()-2 )
12340     {
12341       double x = nodesCoords[i++];
12342       double y = nodesCoords[i++];
12343       double z = nodesCoords[i++];
12344       gp_Pnt p = gp_Pnt(x, y ,z);
12345       gpnts.push_back(p);
12346       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12347       k++;
12348     }
12349   }
12350   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12351   {
12352     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12353     TopTools_IndexedMapOfShape vertexMap;
12354     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12355     gp_Pnt p = gp_Pnt(0,0,0);
12356     if (vertexMap.Extent() < 1)
12357       return;
12358
12359     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12360     {
12361       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12362       p = BRep_Tool::Pnt(vertex);
12363       gpnts.push_back(p);
12364       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12365     }
12366   }
12367
12368   if (gpnts.size() > 0)
12369   {
12370     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12371     //MESSAGE("startNode->nodeId " << nodeId);
12372
12373     double radius2 = radius*radius;
12374     //MESSAGE("radius2 " << radius2);
12375
12376     // --- volumes on start node
12377
12378     setOfVolToCheck.clear();
12379     SMDS_MeshElement* startVol = 0;
12380     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12381     while (volItr->more())
12382     {
12383       startVol = (SMDS_MeshElement*)volItr->next();
12384       setOfVolToCheck.insert(startVol->GetVtkID());
12385     }
12386     if (setOfVolToCheck.empty())
12387     {
12388       MESSAGE("No volumes found");
12389       return;
12390     }
12391
12392     // --- starting with central volumes then their neighbors, check if they are inside
12393     //     or outside the domain, until no more new neighbor volume is inside.
12394     //     Fill the group of inside volumes
12395
12396     std::map<int, double> mapOfNodeDistance2;
12397     std::set<int> setOfOutsideVol;
12398     while (!setOfVolToCheck.empty())
12399     {
12400       std::set<int>::iterator it = setOfVolToCheck.begin();
12401       int vtkId = *it;
12402       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12403       bool volInside = false;
12404       vtkIdType npts = 0;
12405       vtkIdType const *pts(nullptr);
12406       grid->GetCellPoints(vtkId, npts, pts);
12407       for (int i=0; i<npts; i++)
12408       {
12409         double distance2 = 0;
12410         if (mapOfNodeDistance2.count(pts[i]))
12411         {
12412           distance2 = mapOfNodeDistance2[pts[i]];
12413           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12414         }
12415         else
12416         {
12417           double *coords = grid->GetPoint(pts[i]);
12418           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12419           distance2 = 1.E40;
12420           for ( size_t j = 0; j < gpnts.size(); j++ )
12421           {
12422             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12423             if (d2 < distance2)
12424             {
12425               distance2 = d2;
12426               if (distance2 < radius2)
12427                 break;
12428             }
12429           }
12430           mapOfNodeDistance2[pts[i]] = distance2;
12431           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12432         }
12433         if (distance2 < radius2)
12434         {
12435           volInside = true; // one or more nodes inside the domain
12436           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12437           break;
12438         }
12439       }
12440       if (volInside)
12441       {
12442         setOfInsideVol.insert(vtkId);
12443         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12444         int neighborsVtkIds[NBMAXNEIGHBORS];
12445         int downIds[NBMAXNEIGHBORS];
12446         unsigned char downTypes[NBMAXNEIGHBORS];
12447         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12448         for (int n = 0; n < nbNeighbors; n++)
12449           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12450             setOfVolToCheck.insert(neighborsVtkIds[n]);
12451       }
12452       else
12453       {
12454         setOfOutsideVol.insert(vtkId);
12455         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12456       }
12457       setOfVolToCheck.erase(vtkId);
12458     }
12459   }
12460
12461   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12462   //     If yes, add the volume to the inside set
12463
12464   bool addedInside = true;
12465   std::set<int> setOfVolToReCheck;
12466   while (addedInside)
12467   {
12468     //MESSAGE(" --------------------------- re check");
12469     addedInside = false;
12470     std::set<int>::iterator itv = setOfInsideVol.begin();
12471     for (; itv != setOfInsideVol.end(); ++itv)
12472     {
12473       int vtkId = *itv;
12474       int neighborsVtkIds[NBMAXNEIGHBORS];
12475       int downIds[NBMAXNEIGHBORS];
12476       unsigned char downTypes[NBMAXNEIGHBORS];
12477       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12478       for (int n = 0; n < nbNeighbors; n++)
12479         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12480           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12481     }
12482     setOfVolToCheck = setOfVolToReCheck;
12483     setOfVolToReCheck.clear();
12484     while  (!setOfVolToCheck.empty())
12485     {
12486       std::set<int>::iterator it = setOfVolToCheck.begin();
12487       int vtkId = *it;
12488       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12489       {
12490         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12491         int countInside = 0;
12492         int neighborsVtkIds[NBMAXNEIGHBORS];
12493         int downIds[NBMAXNEIGHBORS];
12494         unsigned char downTypes[NBMAXNEIGHBORS];
12495         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12496         for (int n = 0; n < nbNeighbors; n++)
12497           if (setOfInsideVol.count(neighborsVtkIds[n]))
12498             countInside++;
12499         //MESSAGE("countInside " << countInside);
12500         if (countInside > 1)
12501         {
12502           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12503           setOfInsideVol.insert(vtkId);
12504           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12505           addedInside = true;
12506         }
12507         else
12508           setOfVolToReCheck.insert(vtkId);
12509       }
12510       setOfVolToCheck.erase(vtkId);
12511     }
12512   }
12513
12514   // --- map of Downward faces at the boundary, inside the global volume
12515   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12516   //     fill group of SMDS faces inside the volume (when several volume shapes)
12517   //     fill group of SMDS faces on the skin of the global volume (if skin)
12518
12519   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12520   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12521   std::set<int>::iterator it = setOfInsideVol.begin();
12522   for (; it != setOfInsideVol.end(); ++it)
12523   {
12524     int vtkId = *it;
12525     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12526     int neighborsVtkIds[NBMAXNEIGHBORS];
12527     int downIds[NBMAXNEIGHBORS];
12528     unsigned char downTypes[NBMAXNEIGHBORS];
12529     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12530     for (int n = 0; n < nbNeighbors; n++)
12531     {
12532       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12533       if (neighborDim == 3)
12534       {
12535         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12536         {
12537           DownIdType face(downIds[n], downTypes[n]);
12538           boundaryFaces[face] = vtkId;
12539         }
12540         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12541         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12542         if (vtkFaceId >= 0)
12543         {
12544           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12545           // find also the smds edges on this face
12546           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12547           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12548           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12549           for (int i = 0; i < nbEdges; i++)
12550           {
12551             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12552             if (vtkEdgeId >= 0)
12553               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12554           }
12555         }
12556       }
12557       else if (neighborDim == 2) // skin of the volume
12558       {
12559         DownIdType face(downIds[n], downTypes[n]);
12560         skinFaces[face] = vtkId;
12561         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12562         if (vtkFaceId >= 0)
12563           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12564       }
12565     }
12566   }
12567
12568   // --- identify the edges constituting the wire of each subshape on the skin
12569   //     define polylines with the nodes of edges, equivalent to wires
12570   //     project polylines on subshapes, and partition, to get geom faces
12571
12572   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12573   std::set<int>                 shapeIds;
12574
12575   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12576   while (itelem->more())
12577   {
12578     const SMDS_MeshElement *elem = itelem->next();
12579     int shapeId = elem->getshapeId();
12580     int   vtkId = elem->GetVtkID();
12581     if (!shapeIdToVtkIdSet.count(shapeId))
12582     {
12583       shapeIds.insert(shapeId);
12584     }
12585     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12586   }
12587
12588   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12589   std::set<DownIdType, DownIdCompare> emptyEdges;
12590
12591   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12592   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12593   {
12594     int shapeId = itShape->first;
12595     //MESSAGE(" --- Shape ID --- "<< shapeId);
12596     shapeIdToEdges[shapeId] = emptyEdges;
12597
12598     std::vector<int> nodesEdges;
12599
12600     std::set<int>::iterator its = itShape->second.begin();
12601     for (; its != itShape->second.end(); ++its)
12602     {
12603       int vtkId = *its;
12604       //MESSAGE("     " << vtkId);
12605       int neighborsVtkIds[NBMAXNEIGHBORS];
12606       int downIds[NBMAXNEIGHBORS];
12607       unsigned char downTypes[NBMAXNEIGHBORS];
12608       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12609       for (int n = 0; n < nbNeighbors; n++)
12610       {
12611         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12612           continue;
12613         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12614         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12615         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12616         {
12617           DownIdType edge(downIds[n], downTypes[n]);
12618           if (!shapeIdToEdges[shapeId].count(edge))
12619           {
12620             shapeIdToEdges[shapeId].insert(edge);
12621             int vtkNodeId[3];
12622             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12623             nodesEdges.push_back(vtkNodeId[0]);
12624             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12625             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12626           }
12627         }
12628       }
12629     }
12630
12631     std::list<int> order;
12632     if (nodesEdges.size() > 0)
12633     {
12634       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12635       nodesEdges[0] = -1;
12636       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12637       nodesEdges[1] = -1; // do not reuse this edge
12638       bool found = true;
12639       while (found)
12640       {
12641         int nodeTofind = order.back(); // try first to push back
12642         int i = 0;
12643         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12644           if (nodesEdges[i] == nodeTofind)
12645             break;
12646         if ( i == (int) nodesEdges.size() )
12647           found = false; // no follower found on back
12648         else
12649         {
12650           if (i%2) // odd ==> use the previous one
12651             if (nodesEdges[i-1] < 0)
12652               found = false;
12653             else
12654             {
12655               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12656               nodesEdges[i-1] = -1;
12657             }
12658           else // even ==> use the next one
12659             if (nodesEdges[i+1] < 0)
12660               found = false;
12661             else
12662             {
12663               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12664               nodesEdges[i+1] = -1;
12665             }
12666         }
12667         if (found)
12668           continue;
12669         // try to push front
12670         found = true;
12671         nodeTofind = order.front(); // try to push front
12672         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12673           if ( nodesEdges[i] == nodeTofind )
12674             break;
12675         if ( i == (int)nodesEdges.size() )
12676         {
12677           found = false; // no predecessor found on front
12678           continue;
12679         }
12680         if (i%2) // odd ==> use the previous one
12681           if (nodesEdges[i-1] < 0)
12682             found = false;
12683           else
12684           {
12685             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12686             nodesEdges[i-1] = -1;
12687           }
12688         else // even ==> use the next one
12689           if (nodesEdges[i+1] < 0)
12690             found = false;
12691           else
12692           {
12693             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12694             nodesEdges[i+1] = -1;
12695           }
12696       }
12697     }
12698
12699
12700     std::vector<int> nodes;
12701     nodes.push_back(shapeId);
12702     std::list<int>::iterator itl = order.begin();
12703     for (; itl != order.end(); itl++)
12704     {
12705       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12706       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12707     }
12708     listOfListOfNodes.push_back(nodes);
12709   }
12710
12711   //     partition geom faces with blocFissure
12712   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12713   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12714
12715   return;
12716 }
12717
12718
12719 //================================================================================
12720 /*!
12721  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12722  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12723  * \return TRUE if operation has been completed successfully, FALSE otherwise
12724  */
12725 //================================================================================
12726
12727 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12728 {
12729   // iterates on volume elements and detect all free faces on them
12730   SMESHDS_Mesh* aMesh = GetMeshDS();
12731   if (!aMesh)
12732     return false;
12733
12734   ElemFeatures faceType( SMDSAbs_Face );
12735   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12736   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12737   while(vIt->more())
12738   {
12739     const SMDS_MeshVolume* volume = vIt->next();
12740     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12741     vTool.SetExternalNormal();
12742     const int iQuad = volume->IsQuadratic();
12743     faceType.SetQuad( iQuad );
12744     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12745     {
12746       if (!vTool.IsFreeFace(iface))
12747         continue;
12748       nbFree++;
12749       vector<const SMDS_MeshNode *> nodes;
12750       int nbFaceNodes = vTool.NbFaceNodes(iface);
12751       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12752       int inode = 0;
12753       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12754         nodes.push_back(faceNodes[inode]);
12755
12756       if (iQuad) // add medium nodes
12757       {
12758         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12759           nodes.push_back(faceNodes[inode]);
12760         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12761           nodes.push_back(faceNodes[8]);
12762       }
12763       // add new face based on volume nodes
12764       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12765       {
12766         nbExisted++; // face already exists
12767       }
12768       else
12769       {
12770         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12771         nbCreated++;
12772       }
12773     }
12774   }
12775   return ( nbFree == ( nbExisted + nbCreated ));
12776 }
12777
12778 namespace
12779 {
12780   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12781   {
12782     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12783       return n;
12784     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12785   }
12786 }
12787 //================================================================================
12788 /*!
12789  * \brief Creates missing boundary elements
12790  *  \param elements - elements whose boundary is to be checked
12791  *  \param dimension - defines type of boundary elements to create
12792  *  \param group - a group to store created boundary elements in
12793  *  \param targetMesh - a mesh to store created boundary elements in
12794  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12795  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12796  *                                boundary elements will be copied into the targetMesh
12797  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12798  *                                boundary elements will be added into the new group
12799  *  \param aroundElements - if true, elements will be created on boundary of given
12800  *                          elements else, on boundary of the whole mesh.
12801  * \return nb of added boundary elements
12802  */
12803 //================================================================================
12804
12805 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12806                                        Bnd_Dimension           dimension,
12807                                        SMESH_Group*            group/*=0*/,
12808                                        SMESH_Mesh*             targetMesh/*=0*/,
12809                                        bool                    toCopyElements/*=false*/,
12810                                        bool                    toCopyExistingBoundary/*=false*/,
12811                                        bool                    toAddExistingBondary/*= false*/,
12812                                        bool                    aroundElements/*= false*/)
12813 {
12814   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12815   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12816   // hope that all elements are of the same type, do not check them all
12817   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12818     throw SALOME_Exception(LOCALIZED("wrong element type"));
12819
12820   if ( !targetMesh )
12821     toCopyElements = toCopyExistingBoundary = false;
12822
12823   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12824   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12825   int nbAddedBnd = 0;
12826
12827   // editor adding present bnd elements and optionally holding elements to add to the group
12828   SMESH_MeshEditor* presentEditor;
12829   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12830   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12831
12832   SMESH_MesherHelper helper( *myMesh );
12833   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12834   SMDS_VolumeTool vTool;
12835   TIDSortedElemSet avoidSet;
12836   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12837   size_t inode;
12838
12839   typedef vector<const SMDS_MeshNode*> TConnectivity;
12840   TConnectivity tgtNodes;
12841   ElemFeatures elemKind( missType ), elemToCopy;
12842
12843   vector<const SMDS_MeshElement*> presentBndElems;
12844   vector<TConnectivity>           missingBndElems;
12845   vector<int>                     freeFacets;
12846   TConnectivity nodes, elemNodes;
12847
12848   SMDS_ElemIteratorPtr eIt;
12849   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12850   else                  eIt = SMESHUtils::elemSetIterator( elements );
12851
12852   while ( eIt->more() )
12853   {
12854     const SMDS_MeshElement* elem = eIt->next();
12855     const int              iQuad = elem->IsQuadratic();
12856     elemKind.SetQuad( iQuad );
12857
12858     // ------------------------------------------------------------------------------------
12859     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12860     // ------------------------------------------------------------------------------------
12861     presentBndElems.clear();
12862     missingBndElems.clear();
12863     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12864     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12865     {
12866       const SMDS_MeshElement* otherVol = 0;
12867       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12868       {
12869         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12870              ( !aroundElements || elements.count( otherVol )))
12871           continue;
12872         freeFacets.push_back( iface );
12873       }
12874       if ( missType == SMDSAbs_Face )
12875         vTool.SetExternalNormal();
12876       for ( size_t i = 0; i < freeFacets.size(); ++i )
12877       {
12878         int                iface = freeFacets[i];
12879         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12880         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12881         if ( missType == SMDSAbs_Edge ) // boundary edges
12882         {
12883           nodes.resize( 2+iQuad );
12884           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12885           {
12886             for ( size_t j = 0; j < nodes.size(); ++j )
12887               nodes[ j ] = nn[ i+j ];
12888             if ( const SMDS_MeshElement* edge =
12889                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12890               presentBndElems.push_back( edge );
12891             else
12892               missingBndElems.push_back( nodes );
12893           }
12894         }
12895         else // boundary face
12896         {
12897           nodes.clear();
12898           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12899             nodes.push_back( nn[inode] ); // add corner nodes
12900           if (iQuad)
12901             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12902               nodes.push_back( nn[inode] ); // add medium nodes
12903           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12904           if ( iCenter > 0 )
12905             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12906
12907           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12908                                                                SMDSAbs_Face, /*noMedium=*/false ))
12909             presentBndElems.push_back( f );
12910           else
12911             missingBndElems.push_back( nodes );
12912
12913           if ( targetMesh != myMesh )
12914           {
12915             // add 1D elements on face boundary to be added to a new mesh
12916             const SMDS_MeshElement* edge;
12917             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12918             {
12919               if ( iQuad )
12920                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12921               else
12922                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12923               if ( edge && avoidSet.insert( edge ).second )
12924                 presentBndElems.push_back( edge );
12925             }
12926           }
12927         }
12928       }
12929     }
12930     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12931     {
12932       avoidSet.clear(), avoidSet.insert( elem );
12933       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12934                         SMDS_MeshElement::iterator() );
12935       elemNodes.push_back( elemNodes[0] );
12936       nodes.resize( 2 + iQuad );
12937       const int nbLinks = elem->NbCornerNodes();
12938       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12939       {
12940         nodes[0] = elemNodes[iN];
12941         nodes[1] = elemNodes[iN+1+iQuad];
12942         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12943           continue; // not free link
12944
12945         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12946         if ( const SMDS_MeshElement* edge =
12947              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12948           presentBndElems.push_back( edge );
12949         else
12950           missingBndElems.push_back( nodes );
12951       }
12952     }
12953
12954     // ---------------------------------
12955     // 2. Add missing boundary elements
12956     // ---------------------------------
12957     if ( targetMesh != myMesh )
12958       // instead of making a map of nodes in this mesh and targetMesh,
12959       // we create nodes with same IDs.
12960       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12961       {
12962         TConnectivity& srcNodes = missingBndElems[i];
12963         tgtNodes.resize( srcNodes.size() );
12964         for ( inode = 0; inode < srcNodes.size(); ++inode )
12965           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12966         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12967                                                                        missType,
12968                                                                        /*noMedium=*/false))
12969           continue;
12970         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12971         ++nbAddedBnd;
12972       }
12973     else
12974       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12975       {
12976         TConnectivity& nodes = missingBndElems[ i ];
12977         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12978                                                                        missType,
12979                                                                        /*noMedium=*/false))
12980           continue;
12981         SMDS_MeshElement* newElem =
12982           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12983         nbAddedBnd += bool( newElem );
12984
12985         // try to set a new element to a shape
12986         if ( myMesh->HasShapeToMesh() )
12987         {
12988           bool ok = true;
12989           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12990           const size_t nbN = nodes.size() / (iQuad+1 );
12991           for ( inode = 0; inode < nbN && ok; ++inode )
12992           {
12993             pair<int, TopAbs_ShapeEnum> i_stype =
12994               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12995             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12996               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12997           }
12998           if ( ok && mediumShapes.size() > 1 )
12999           {
13000             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13001             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13002             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13003             {
13004               if (( ok = ( stype_i->first != stype_i_0.first )))
13005                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13006                                         aMesh->IndexToShape( stype_i_0.second ));
13007             }
13008           }
13009           if ( ok && mediumShapes.begin()->first == missShapeType )
13010             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13011         }
13012       }
13013
13014     // ----------------------------------
13015     // 3. Copy present boundary elements
13016     // ----------------------------------
13017     if ( toCopyExistingBoundary )
13018       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13019       {
13020         const SMDS_MeshElement* e = presentBndElems[i];
13021         tgtNodes.resize( e->NbNodes() );
13022         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13023           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13024         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13025       }
13026     else // store present elements to add them to a group
13027       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13028       {
13029         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13030       }
13031
13032   } // loop on given elements
13033
13034   // ---------------------------------------------
13035   // 4. Fill group with boundary elements
13036   // ---------------------------------------------
13037   if ( group )
13038   {
13039     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13040       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13041         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13042   }
13043   tgtEditor.myLastCreatedElems.clear();
13044   tgtEditor2.myLastCreatedElems.clear();
13045
13046   // -----------------------
13047   // 5. Copy given elements
13048   // -----------------------
13049   if ( toCopyElements && targetMesh != myMesh )
13050   {
13051     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13052     else                  eIt = SMESHUtils::elemSetIterator( elements );
13053     while (eIt->more())
13054     {
13055       const SMDS_MeshElement* elem = eIt->next();
13056       tgtNodes.resize( elem->NbNodes() );
13057       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13058         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13059       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13060
13061       tgtEditor.myLastCreatedElems.clear();
13062     }
13063   }
13064   return nbAddedBnd;
13065 }
13066
13067 //================================================================================
13068 /*!
13069  * \brief Copy node position and set \a to node on the same geometry
13070  */
13071 //================================================================================
13072
13073 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13074                                      const SMDS_MeshNode* to )
13075 {
13076   if ( !from || !to ) return;
13077
13078   SMDS_PositionPtr pos = from->GetPosition();
13079   if ( !pos || from->getshapeId() < 1 ) return;
13080
13081   switch ( pos->GetTypeOfPosition() )
13082   {
13083   case SMDS_TOP_3DSPACE: break;
13084
13085   case SMDS_TOP_FACE:
13086   {
13087     SMDS_FacePositionPtr fPos = pos;
13088     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13089                                 fPos->GetUParameter(), fPos->GetVParameter() );
13090     break;
13091   }
13092   case SMDS_TOP_EDGE:
13093   {
13094     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13095     SMDS_EdgePositionPtr ePos = pos;
13096     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13097     break;
13098   }
13099   case SMDS_TOP_VERTEX:
13100   {
13101     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13102     break;
13103   }
13104   case SMDS_TOP_UNSPEC:
13105   default:;
13106   }
13107 }