Salome HOME
Porting Salome to OCCT 7.7.0
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #include <smIdType.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 //=======================================================================
111 //function : SMESH_MeshEditor
112 //purpose  :
113 //=======================================================================
114
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116   :myMesh( theMesh ) // theMesh may be NULL
117 {
118 }
119
120 //================================================================================
121 /*!
122  * \brief Return mesh DS
123  */
124 //================================================================================
125
126 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
127 {
128   return myMesh->GetMeshDS();
129 }
130
131
132 //================================================================================
133 /*!
134  * \brief Clears myLastCreatedNodes and myLastCreatedElems
135  */
136 //================================================================================
137
138 void SMESH_MeshEditor::ClearLastCreated()
139 {
140   SMESHUtils::FreeVector( myLastCreatedElems );
141   SMESHUtils::FreeVector( myLastCreatedNodes );
142 }
143
144 //================================================================================
145 /*!
146  * \brief Initializes members by an existing element
147  *  \param [in] elem - the source element
148  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
149  */
150 //================================================================================
151
152 SMESH_MeshEditor::ElemFeatures&
153 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
154 {
155   if ( elem )
156   {
157     myType = elem->GetType();
158     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
159     {
160       myIsPoly = elem->IsPoly();
161       if ( myIsPoly )
162       {
163         myIsQuad = elem->IsQuadratic();
164         if ( myType == SMDSAbs_Volume && !basicOnly )
165         {
166           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const smIdType ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],node[13],node[14],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12],node[13],node[14] );
300       }
301       else if (nbnode == 18) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],
306                                                  node[15],node[16],node[17],ID );
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],
311                                                  node[15],node[16],node[17] );
312       }
313       else if (nbnode == 20) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19] );
324       }
325       else if (nbnode == 27) {
326         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327                                                  node[4], node[5], node[6], node[7],
328                                                  node[8], node[9], node[10],node[11],
329                                                  node[12],node[13],node[14],node[15],
330                                                  node[16],node[17],node[18],node[19],
331                                                  node[20],node[21],node[22],node[23],
332                                                  node[24],node[25],node[26], ID);
333         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
334                                                  node[4], node[5], node[6], node[7],
335                                                  node[8], node[9], node[10],node[11],
336                                                  node[12],node[13],node[14],node[15],
337                                                  node[16],node[17],node[18],node[19],
338                                                  node[20],node[21],node[22],node[23],
339                                                  node[24],node[25],node[26] );
340       }
341     }
342     else if ( !features.myIsQuad )
343     {
344       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
346     }
347     else
348     {
349       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
351     }
352     break;
353
354   case SMDSAbs_Edge:
355     if ( nbnode == 2 ) {
356       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357       else           e = mesh->AddEdge      (node[0], node[1] );
358     }
359     else if ( nbnode == 3 ) {
360       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
362     }
363     break;
364
365   case SMDSAbs_0DElement:
366     if ( nbnode == 1 ) {
367       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368       else           e = mesh->Add0DElement      (node[0] );
369     }
370     break;
371
372   case SMDSAbs_Node:
373     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
375     break;
376
377   case SMDSAbs_Ball:
378     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
380     break;
381
382   default:;
383   }
384   if ( e ) myLastCreatedElems.push_back( e );
385   return e;
386 }
387
388 //=======================================================================
389 /*!
390  * \brief Add element
391  */
392 //=======================================================================
393
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
395                                                const ElemFeatures&      features)
396 {
397   vector<const SMDS_MeshNode*> nodes;
398   nodes.reserve( nodeIDs.size() );
399   vector<smIdType>::const_iterator id = nodeIDs.begin();
400   while ( id != nodeIDs.end() ) {
401     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402       nodes.push_back( node );
403     else
404       return 0;
405   }
406   return AddElement( nodes, features );
407 }
408
409 //=======================================================================
410 //function : Remove
411 //purpose  : Remove a node or an element.
412 //           Modify a compute state of sub-meshes which become empty
413 //=======================================================================
414
415 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
416                                    const bool                   isNodes )
417 {
418   ClearLastCreated();
419
420   SMESHDS_Mesh* aMesh = GetMeshDS();
421   set< SMESH_subMesh *> smmap;
422
423   smIdType removed = 0;
424   list<smIdType>::const_iterator it = theIDs.begin();
425   for ( ; it != theIDs.end(); it++ ) {
426     const SMDS_MeshElement * elem;
427     if ( isNodes )
428       elem = aMesh->FindNode( *it );
429     else
430       elem = aMesh->FindElement( *it );
431     if ( !elem )
432       continue;
433
434     // Notify VERTEX sub-meshes about modification
435     if ( isNodes ) {
436       const SMDS_MeshNode* node = cast2Node( elem );
437       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438         if ( int aShapeID = node->getshapeId() )
439           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440             smmap.insert( sm );
441     }
442     // Find sub-meshes to notify about modification
443     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444     //     while ( nodeIt->more() ) {
445     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
447     //       if ( aPosition.get() ) {
448     //         if ( int aShapeID = aPosition->GetShapeId() ) {
449     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450     //             smmap.insert( sm );
451     //         }
452     //       }
453     //     }
454
455     // Do remove
456     if ( isNodes )
457       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
458     else
459       aMesh->RemoveElement( elem );
460     removed++;
461   }
462
463   // Notify sub-meshes about modification
464   if ( !smmap.empty() ) {
465     set< SMESH_subMesh *>::iterator smIt;
466     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468   }
469
470   //   // Check if the whole mesh becomes empty
471   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
473
474   return removed;
475 }
476
477 //================================================================================
478 /*!
479  * \brief Remove a node and fill a hole appeared, by changing surrounding faces
480  */
481 //================================================================================
482
483 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
484 {
485   if ( ! node )
486     return;
487
488   if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
489     throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
490
491   // check that only triangles surround the node
492   for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
493   {
494     const SMDS_MeshElement* face = fIt->next();
495     if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
496       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
497     if ( face->IsQuadratic() )
498       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
499   }
500
501   std::vector< const SMDS_MeshNode*> neighbours(2);
502   SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
503
504   bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
505
506   // if ( neighbours.size() == 2 ) // on boundary
507   // {
508   //   // check if theNode and neighbours are on a line
509   //   gp_Pnt pN = SMESH_NodeXYZ( node );
510   //   gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
511   //   gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
512   //   double dist01 = p0.Distance( p1 );
513   //   double    tol = 0.01 * dist01;
514   //   double  distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
515   //   bool   onLine = distN < tol;
516   //   toRemove = !onLine;
517   // }
518
519   if ( neighbours.empty() ) // not on boundary
520   {
521     TIDSortedElemSet linkedNodes;
522     GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
523     for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
524     if ( neighbours.empty() )
525       toRemove = true;
526   }
527
528   if ( toRemove )
529   {
530     this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
531     return;
532   }
533
534   // choose a node to replace by
535   const SMDS_MeshNode* nToReplace = nullptr;
536   SMESH_NodeXYZ           nodeXYZ = node;
537   double                  minDist = Precision::Infinite();
538   for ( const SMDS_MeshNode* n : neighbours )
539   {
540     double dist = nodeXYZ.SquareDistance( n );
541     if ( dist < minDist )
542     {
543       minDist = dist;
544       nToReplace = n;
545     }
546   }
547
548   // remove node + replace by nToReplace
549   std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
550   TListOfListOfNodes nodesToMerge( 1, nodeGroup );
551   this->MergeNodes( nodesToMerge );
552 }
553
554 //================================================================================
555 /*!
556  * \brief Create 0D elements on all nodes of the given object.
557  *  \param elements - Elements on whose nodes to create 0D elements; if empty,
558  *                    the all mesh is treated
559  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
560  *  \param duplicateElements - to add one more 0D element to a node or not
561  */
562 //================================================================================
563
564 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
565                                                    TIDSortedElemSet&       all0DElems,
566                                                    const bool              duplicateElements )
567 {
568   SMDS_ElemIteratorPtr elemIt;
569   if ( elements.empty() )
570   {
571     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
572   }
573   else
574   {
575     elemIt = SMESHUtils::elemSetIterator( elements );
576   }
577
578   while ( elemIt->more() )
579   {
580     const SMDS_MeshElement* e = elemIt->next();
581     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
582     while ( nodeIt->more() )
583     {
584       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
585       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
586       if ( duplicateElements || !it0D->more() )
587       {
588         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
589         all0DElems.insert( myLastCreatedElems.back() );
590       }
591       while ( it0D->more() )
592         all0DElems.insert( it0D->next() );
593     }
594   }
595 }
596
597 //=======================================================================
598 //function : FindShape
599 //purpose  : Return an index of the shape theElem is on
600 //           or zero if a shape not found
601 //=======================================================================
602
603 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
604 {
605   ClearLastCreated();
606
607   SMESHDS_Mesh * aMesh = GetMeshDS();
608   if ( aMesh->ShapeToMesh().IsNull() )
609     return 0;
610
611   int aShapeID = theElem->getshapeId();
612   if ( aShapeID < 1 )
613     return 0;
614
615   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
616     if ( sm->Contains( theElem ))
617       return aShapeID;
618
619   if ( theElem->GetType() == SMDSAbs_Node ) {
620     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
621   }
622   else {
623     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
624   }
625
626   TopoDS_Shape aShape; // the shape a node of theElem is on
627   if ( theElem->GetType() != SMDSAbs_Node )
628   {
629     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
630     while ( nodeIt->more() ) {
631       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
632       if ((aShapeID = node->getshapeId()) > 0) {
633         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
634           if ( sm->Contains( theElem ))
635             return aShapeID;
636           if ( aShape.IsNull() )
637             aShape = aMesh->IndexToShape( aShapeID );
638         }
639       }
640     }
641   }
642
643   // None of nodes is on a proper shape,
644   // find the shape among ancestors of aShape on which a node is
645   if ( !aShape.IsNull() ) {
646     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
647     for ( ; ancIt.More(); ancIt.Next() ) {
648       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
649       if ( sm && sm->Contains( theElem ))
650         return aMesh->ShapeToIndex( ancIt.Value() );
651     }
652   }
653   else
654   {
655     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
656     while ( const SMESHDS_SubMesh* sm = smIt->next() )
657       if ( sm->Contains( theElem ))
658         return sm->GetID();
659   }
660
661   return 0;
662 }
663
664 //=======================================================================
665 //function : IsMedium
666 //purpose  :
667 //=======================================================================
668
669 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
670                                 const SMDSAbs_ElementType typeToCheck)
671 {
672   bool isMedium = false;
673   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
674   while (it->more() && !isMedium ) {
675     const SMDS_MeshElement* elem = it->next();
676     isMedium = elem->IsMediumNode(node);
677   }
678   return isMedium;
679 }
680
681 //=======================================================================
682 //function : shiftNodesQuadTria
683 //purpose  : Shift nodes in the array corresponded to quadratic triangle
684 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
685 //=======================================================================
686
687 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
688 {
689   const SMDS_MeshNode* nd1 = aNodes[0];
690   aNodes[0] = aNodes[1];
691   aNodes[1] = aNodes[2];
692   aNodes[2] = nd1;
693   const SMDS_MeshNode* nd2 = aNodes[3];
694   aNodes[3] = aNodes[4];
695   aNodes[4] = aNodes[5];
696   aNodes[5] = nd2;
697 }
698
699 //=======================================================================
700 //function : getNodesFromTwoTria
701 //purpose  : 
702 //=======================================================================
703
704 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
705                                 const SMDS_MeshElement * theTria2,
706                                 vector< const SMDS_MeshNode*>& N1,
707                                 vector< const SMDS_MeshNode*>& N2)
708 {
709   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
710   if ( N1.size() < 6 ) return false;
711   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
712   if ( N2.size() < 6 ) return false;
713
714   int sames[3] = {-1,-1,-1};
715   int nbsames = 0;
716   int i, j;
717   for(i=0; i<3; i++) {
718     for(j=0; j<3; j++) {
719       if(N1[i]==N2[j]) {
720         sames[i] = j;
721         nbsames++;
722         break;
723       }
724     }
725   }
726   if(nbsames!=2) return false;
727   if(sames[0]>-1) {
728     shiftNodesQuadTria(N1);
729     if(sames[1]>-1) {
730       shiftNodesQuadTria(N1);
731     }
732   }
733   i = sames[0] + sames[1] + sames[2];
734   for(; i<2; i++) {
735     shiftNodesQuadTria(N2);
736   }
737   // now we receive following N1 and N2 (using numeration as in the image below)
738   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
739   // i.e. first nodes from both arrays form a new diagonal
740   return true;
741 }
742
743 //=======================================================================
744 //function : InverseDiag
745 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
746 //           but having other common link.
747 //           Return False if args are improper
748 //=======================================================================
749
750 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
751                                     const SMDS_MeshElement * theTria2 )
752 {
753   ClearLastCreated();
754
755   if ( !theTria1 || !theTria2 ||
756        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
757        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
758        theTria1->GetType() != SMDSAbs_Face ||
759        theTria2->GetType() != SMDSAbs_Face )
760     return false;
761
762   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
763       (theTria2->GetEntityType() == SMDSEntity_Triangle))
764   {
765     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
766     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
767     //    |/ |                                         | \|
768     //  B +--+ 2                                     B +--+ 2
769
770     // put nodes in array and find out indices of the same ones
771     const SMDS_MeshNode* aNodes [6];
772     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
773     int i = 0;
774     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
775     while ( it->more() ) {
776       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
777
778       if ( i > 2 ) // theTria2
779         // find same node of theTria1
780         for ( int j = 0; j < 3; j++ )
781           if ( aNodes[ i ] == aNodes[ j ]) {
782             sameInd[ j ] = i;
783             sameInd[ i ] = j;
784             break;
785           }
786       // next
787       i++;
788       if ( i == 3 ) {
789         if ( it->more() )
790           return false; // theTria1 is not a triangle
791         it = theTria2->nodesIterator();
792       }
793       if ( i == 6 && it->more() )
794         return false; // theTria2 is not a triangle
795     }
796
797     // find indices of 1,2 and of A,B in theTria1
798     int iA = -1, iB = 0, i1 = 0, i2 = 0;
799     for ( i = 0; i < 6; i++ ) {
800       if ( sameInd [ i ] == -1 ) {
801         if ( i < 3 ) i1 = i;
802         else         i2 = i;
803       }
804       else if (i < 3) {
805         if ( iA >= 0) iB = i;
806         else          iA = i;
807       }
808     }
809     // nodes 1 and 2 should not be the same
810     if ( aNodes[ i1 ] == aNodes[ i2 ] )
811       return false;
812
813     // theTria1: A->2
814     aNodes[ iA ] = aNodes[ i2 ];
815     // theTria2: B->1
816     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
817
818     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
819     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
820
821     return true;
822
823   } // end if(F1 && F2)
824
825   // check case of quadratic faces
826   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
827       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
828     return false;
829   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
830       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
831     return false;
832
833   //       5
834   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
835   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
836   //    |   / |
837   //  7 +  +  + 6
838   //    | /9  |
839   //    |/    |
840   //  4 +--+--+ 3
841   //       8
842
843   vector< const SMDS_MeshNode* > N1;
844   vector< const SMDS_MeshNode* > N2;
845   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
846     return false;
847   // now we receive following N1 and N2 (using numeration as above image)
848   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
849   // i.e. first nodes from both arrays determ new diagonal
850
851   vector< const SMDS_MeshNode*> N1new( N1.size() );
852   vector< const SMDS_MeshNode*> N2new( N2.size() );
853   N1new.back() = N1.back(); // central node of biquadratic
854   N2new.back() = N2.back();
855   N1new[0] = N1[0];  N2new[0] = N1[0];
856   N1new[1] = N2[0];  N2new[1] = N1[1];
857   N1new[2] = N2[1];  N2new[2] = N2[0];
858   N1new[3] = N1[4];  N2new[3] = N1[3];
859   N1new[4] = N2[3];  N2new[4] = N2[5];
860   N1new[5] = N1[5];  N2new[5] = N1[4];
861   // change nodes in faces
862   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
863   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
864
865   // move the central node of biquadratic triangle
866   SMESH_MesherHelper helper( *GetMesh() );
867   for ( int is2nd = 0; is2nd < 2; ++is2nd )
868   {
869     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
870     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
871     if ( nodes.size() < 7 )
872       continue;
873     helper.SetSubShape( tria->getshapeId() );
874     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
875     gp_Pnt xyz;
876     if ( F.IsNull() )
877     {
878       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
879               SMESH_NodeXYZ( nodes[4] ) +
880               SMESH_NodeXYZ( nodes[5] )) / 3.;
881     }
882     else
883     {
884       bool checkUV;
885       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
886                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
887                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
888       TopLoc_Location loc;
889       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
890       xyz = S->Value( uv.X(), uv.Y() );
891       xyz.Transform( loc );
892       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
893            nodes[6]->getshapeId() > 0 )
894         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
895     }
896     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
897   }
898   return true;
899 }
900
901 //=======================================================================
902 //function : findTriangles
903 //purpose  : find triangles sharing theNode1-theNode2 link
904 //=======================================================================
905
906 static bool findTriangles(const SMDS_MeshNode *    theNode1,
907                           const SMDS_MeshNode *    theNode2,
908                           const SMDS_MeshElement*& theTria1,
909                           const SMDS_MeshElement*& theTria2)
910 {
911   if ( !theNode1 || !theNode2 ) return false;
912
913   theTria1 = theTria2 = 0;
914
915   set< const SMDS_MeshElement* > emap;
916   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
917   while (it->more()) {
918     const SMDS_MeshElement* elem = it->next();
919     if ( elem->NbCornerNodes() == 3 )
920       emap.insert( elem );
921   }
922   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
923   while (it->more()) {
924     const SMDS_MeshElement* elem = it->next();
925     if ( emap.count( elem )) {
926       if ( !theTria1 )
927       {
928         theTria1 = elem;
929       }
930       else  
931       {
932         theTria2 = elem;
933         // theTria1 must be element with minimum ID
934         if ( theTria2->GetID() < theTria1->GetID() )
935           std::swap( theTria2, theTria1 );
936         return true;
937       }
938     }
939   }
940   return false;
941 }
942
943 //=======================================================================
944 //function : InverseDiag
945 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
946 //           with ones built on the same 4 nodes but having other common link.
947 //           Return false if proper faces not found
948 //=======================================================================
949
950 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
951                                     const SMDS_MeshNode * theNode2)
952 {
953   ClearLastCreated();
954
955   const SMDS_MeshElement *tr1, *tr2;
956   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
957     return false;
958
959   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
960        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
961     return false;
962
963   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
964       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
965
966     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
967     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
968     //    |/ |                                    | \|
969     //  B +--+ 2                                B +--+ 2
970
971     // put nodes in array
972     // and find indices of 1,2 and of A in tr1 and of B in tr2
973     int i, iA1 = 0, i1 = 0;
974     const SMDS_MeshNode* aNodes1 [3];
975     SMDS_ElemIteratorPtr it;
976     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
977       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
978       if ( aNodes1[ i ] == theNode1 )
979         iA1 = i; // node A in tr1
980       else if ( aNodes1[ i ] != theNode2 )
981         i1 = i;  // node 1
982     }
983     int iB2 = 0, i2 = 0;
984     const SMDS_MeshNode* aNodes2 [3];
985     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
986       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
987       if ( aNodes2[ i ] == theNode2 )
988         iB2 = i; // node B in tr2
989       else if ( aNodes2[ i ] != theNode1 )
990         i2 = i;  // node 2
991     }
992
993     // nodes 1 and 2 should not be the same
994     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
995       return false;
996
997     // tr1: A->2
998     aNodes1[ iA1 ] = aNodes2[ i2 ];
999     // tr2: B->1
1000     aNodes2[ iB2 ] = aNodes1[ i1 ];
1001
1002     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1003     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1004
1005     return true;
1006   }
1007
1008   // check case of quadratic faces
1009   return InverseDiag(tr1,tr2);
1010 }
1011
1012 //=======================================================================
1013 //function : getQuadrangleNodes
1014 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
1015 //           fusion of triangles tr1 and tr2 having shared link on
1016 //           theNode1 and theNode2
1017 //=======================================================================
1018
1019 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
1020                         const SMDS_MeshNode *    theNode1,
1021                         const SMDS_MeshNode *    theNode2,
1022                         const SMDS_MeshElement * tr1,
1023                         const SMDS_MeshElement * tr2 )
1024 {
1025   if( tr1->NbNodes() != tr2->NbNodes() )
1026     return false;
1027
1028   // find the 4-th node to insert into tr1
1029   const SMDS_MeshNode* n4 = 0;
1030   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1031   for ( int i = 0; !n4 && i < 3; ++i )
1032   {
1033     const SMDS_MeshNode * n = cast2Node( it->next() );
1034     bool isDiag = ( n == theNode1 || n == theNode2 );
1035     if ( !isDiag )
1036       n4 = n;
1037   }
1038
1039   // Make an array of nodes to be in a quadrangle
1040   int iNode = 0, iFirstDiag = -1;
1041   it = tr1->nodesIterator();
1042   for ( int i = 0; i < 3; ++i )
1043   {
1044     const SMDS_MeshNode * n = cast2Node( it->next() );
1045     bool isDiag = ( n == theNode1 || n == theNode2 );
1046     if ( isDiag ) {
1047       if ( iFirstDiag < 0 )
1048         iFirstDiag = iNode;
1049       else if ( iNode - iFirstDiag == 1 )
1050         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1051     }
1052     else if ( n == n4 ) {
1053       return false; // tr1 and tr2 should not have all the same nodes
1054     }
1055     theQuadNodes[ iNode++ ] = n;
1056   }
1057   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1058     theQuadNodes[ iNode ] = n4;
1059
1060   return true;
1061 }
1062
1063 //=======================================================================
1064 //function : DeleteDiag
1065 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1066 //           with a quadrangle built on the same 4 nodes.
1067 //           Return false if proper faces not found
1068 //=======================================================================
1069
1070 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1071                                    const SMDS_MeshNode * theNode2)
1072 {
1073   ClearLastCreated();
1074
1075   const SMDS_MeshElement *tr1, *tr2;
1076   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1077     return false;
1078
1079   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1080        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1081     return false;
1082
1083   SMESHDS_Mesh * aMesh = GetMeshDS();
1084
1085   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1086       (tr2->GetEntityType() == SMDSEntity_Triangle))
1087   {
1088     const SMDS_MeshNode* aNodes [ 4 ];
1089     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1090       return false;
1091
1092     const SMDS_MeshElement* newElem = 0;
1093     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1094     myLastCreatedElems.push_back(newElem);
1095     AddToSameGroups( newElem, tr1, aMesh );
1096     int aShapeId = tr1->getshapeId();
1097     if ( aShapeId )
1098       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1099
1100     aMesh->RemoveElement( tr1 );
1101     aMesh->RemoveElement( tr2 );
1102
1103     return true;
1104   }
1105
1106   // check case of quadratic faces
1107   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1108     return false;
1109   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1110     return false;
1111
1112   //       5
1113   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1114   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1115   //    |   / |
1116   //  7 +  +  + 6
1117   //    | /9  |
1118   //    |/    |
1119   //  4 +--+--+ 3
1120   //       8
1121
1122   vector< const SMDS_MeshNode* > N1;
1123   vector< const SMDS_MeshNode* > N2;
1124   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1125     return false;
1126   // now we receive following N1 and N2 (using numeration as above image)
1127   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1128   // i.e. first nodes from both arrays determ new diagonal
1129
1130   const SMDS_MeshNode* aNodes[8];
1131   aNodes[0] = N1[0];
1132   aNodes[1] = N1[1];
1133   aNodes[2] = N2[0];
1134   aNodes[3] = N2[1];
1135   aNodes[4] = N1[3];
1136   aNodes[5] = N2[5];
1137   aNodes[6] = N2[3];
1138   aNodes[7] = N1[5];
1139
1140   const SMDS_MeshElement* newElem = 0;
1141   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1142                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1143   myLastCreatedElems.push_back(newElem);
1144   AddToSameGroups( newElem, tr1, aMesh );
1145   int aShapeId = tr1->getshapeId();
1146   if ( aShapeId )
1147   {
1148     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1149   }
1150   aMesh->RemoveElement( tr1 );
1151   aMesh->RemoveElement( tr2 );
1152
1153   // remove middle node (9)
1154   GetMeshDS()->RemoveNode( N1[4] );
1155
1156   return true;
1157 }
1158
1159 //=======================================================================
1160 //function : SplitEdge
1161 //purpose  : Replace each triangle bound by theNode1-theNode2 segment with
1162 //           two triangles by connecting a node made on the link with a node opposite to the link.
1163 //=======================================================================
1164
1165 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1166                                   const SMDS_MeshNode * theNode2,
1167                                   double                thePosition)
1168 {
1169   ClearLastCreated();
1170
1171   SMESHDS_Mesh * mesh = GetMeshDS();
1172
1173   // Get triangles and segments to divide
1174
1175   std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1176   std::vector<const SMDS_MeshElement *> foundElems;
1177   if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1178     throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1179                             << theNode1->GetID() << " - " << theNode2->GetID());
1180
1181   SMESH_MesherHelper helper( *GetMesh() );
1182
1183   for ( const SMDS_MeshElement * elem : foundElems )
1184   {
1185     SMDSAbs_ElementType type = elem->GetType();
1186     switch ( type ) {
1187     case SMDSAbs_Volume:
1188       throw SALOME_Exception( "Can't split an edge of a volume");
1189       break;
1190
1191     case SMDSAbs_Face:
1192       if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1193         throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1194       if ( elem->IsQuadratic() )
1195       {
1196         helper.SetIsQuadratic( true );
1197         helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1198         helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1199       }
1200       break;
1201
1202     case SMDSAbs_Edge:
1203       if ( elem->IsQuadratic() )
1204       {
1205         helper.SetIsQuadratic( true );
1206         helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1207       }
1208       break;
1209     default:;
1210     }
1211   }
1212
1213   // Make a new node
1214
1215   const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1216
1217   gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1218                         SMESH_NodeXYZ( theNode2 ) * thePosition );
1219
1220   const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1221   if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1222   {
1223     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1224     double  tol = 100 * helper.MaxTolerance( S );
1225     gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1226     if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1227     {
1228       newNodeXYZ = surface->Value( uv );
1229       if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1230         nPos->SetParameters( uv.X(), uv.Y() );
1231     }
1232   }
1233   if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1234   {
1235     mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1236     double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1237     helper.ToFixNodeParameters( true );
1238     if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1239       newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1240   }
1241   mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1242
1243   // Split triangles and segments
1244
1245   std::vector<const SMDS_MeshNode *> nodes( 7 );
1246   for ( const SMDS_MeshElement * elem : foundElems )
1247   {
1248     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1249     nodes.resize( elem->NbCornerNodes() + 1 );
1250     nodes.back() = nodes[0];
1251
1252     smIdType id = elem->GetID();
1253     int shapeID = elem->GetShapeID();
1254
1255     const SMDS_MeshNode* centralNode = nullptr;
1256     if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1257       centralNode = elem->GetNode( 6 );
1258
1259     mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1260     if ( centralNode )
1261       mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1262
1263     for ( size_t i = 1; i < nodes.size(); ++i )
1264     {
1265       const SMDS_MeshNode* n1 = nodes[i-1];
1266       const SMDS_MeshNode* n2 = nodes[i];
1267       const SMDS_MeshElement* newElem;
1268       if ( nodes.size() == 4 ) //    triangle
1269       {
1270         bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1271         bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1272         if ( isDiag1 && isDiag2 )
1273           continue;
1274
1275         newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1276       }
1277       else //    segment
1278       {
1279         newElem = helper.AddEdge( n1, nodeOnLink, id );
1280       }
1281       myLastCreatedElems.push_back( newElem );
1282       AddToSameGroups( newElem, elem, mesh );
1283       if ( shapeID )
1284         mesh->SetMeshElementOnShape( newElem, shapeID );
1285       id = 0;
1286     }
1287   }
1288   return;
1289 }
1290
1291 //=======================================================================
1292 //function : SplitFace
1293 //purpose  : Split a face into triangles each formed by two nodes of the 
1294 //           face and a new node added at the given coordinates.
1295 //=======================================================================
1296
1297 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1298                                   double                   theX,
1299                                   double                   theY,
1300                                   double                   theZ )
1301 {
1302   ClearLastCreated();
1303
1304   if ( !theFace )
1305     throw SALOME_Exception("Null face given");
1306   if ( theFace->GetType() != SMDSAbs_Face )
1307     throw SALOME_Exception("Not a face given");
1308
1309   SMESHDS_Mesh * mesh = GetMeshDS();
1310
1311   SMESH_MesherHelper helper( *GetMesh() );
1312   if ( theFace->IsQuadratic() )
1313   {
1314     helper.SetIsQuadratic( true );
1315     helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1316   }
1317   const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1318   helper.SetSubShape( shape );
1319   helper.SetElementsOnShape( true );
1320
1321   // Make a new node
1322
1323   const SMDS_MeshNode* centralNode = nullptr;
1324   if (      theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1325     centralNode = theFace->GetNode( 6 );
1326   else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1327     centralNode = theFace->GetNode( 8 );
1328
1329   if ( centralNode )
1330   {
1331     helper.SetIsBiQuadratic( true );
1332     mesh->MoveNode( centralNode, theX, theY, theZ );
1333   }
1334   else
1335     centralNode = helper.AddNode( theX, theY, theZ );
1336
1337
1338   // Split theFace
1339
1340   std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1341   nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1342   nodes.resize( theFace->NbCornerNodes() + 1 );
1343   nodes.back() = nodes[0];
1344
1345   smIdType id = theFace->GetID();
1346   int shapeID = theFace->GetShapeID();
1347
1348   mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1349
1350   for ( size_t i = 1; i < nodes.size(); ++i )
1351   {
1352     const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1353
1354     myLastCreatedElems.push_back( newElem );
1355     AddToSameGroups( newElem, theFace, mesh );
1356     if ( shapeID )
1357       mesh->SetMeshElementOnShape( newElem, shapeID );
1358     id = 0;
1359   }
1360   return;
1361 }
1362
1363 //=======================================================================
1364 //function : Reorient
1365 //purpose  : Reverse theElement orientation
1366 //=======================================================================
1367
1368 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1369 {
1370   ClearLastCreated();
1371
1372   if (!theElem)
1373     return false;
1374   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1375   if ( !it || !it->more() )
1376     return false;
1377
1378   const SMDSAbs_ElementType type = theElem->GetType();
1379   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1380     return false;
1381
1382   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1383   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1384   {
1385     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1386     if (!aPolyedre) {
1387       MESSAGE("Warning: bad volumic element");
1388       return false;
1389     }
1390     SMDS_VolumeTool vTool( aPolyedre );
1391     const int nbFaces = vTool.NbFaces();
1392     vector<int> quantities( nbFaces );
1393     vector<const SMDS_MeshNode *> poly_nodes;
1394
1395     // check if all facets are oriented equally
1396     bool sameOri = true;
1397     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1398     for (int iface = 0; iface < nbFaces; iface++)
1399     {
1400       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1401       if ( facetOri[ iface ] != facetOri[ 0 ])
1402         sameOri = false;
1403     }
1404
1405     // reverse faces of the polyhedron
1406     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1407     poly_nodes.reserve( vTool.NbNodes() );
1408     for ( int iface = 0; iface < nbFaces; iface++ )
1409     {
1410       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1411       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1412       bool toReverse = ( facetOri[ iface ] != neededOri );
1413
1414       quantities[ iface ] = nbFaceNodes;
1415
1416       if ( toReverse )
1417         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1418           poly_nodes.push_back( nodes[ inode ]);
1419       else
1420         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1421     }
1422     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1423   }
1424   else // other elements
1425   {
1426     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1427     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1428     if ( interlace.empty() )
1429     {
1430       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1431     }
1432     else
1433     {
1434       SMDS_MeshCell::applyInterlace( interlace, nodes );
1435     }
1436     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1437   }
1438   return false;
1439 }
1440
1441 //================================================================================
1442 /*!
1443  * \brief Reorient faces.
1444  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1445  * \param theDirection - desired direction of normal of \a theRefFaces.
1446  *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1447  * \param theRefFaces - correctly oriented faces whose orientation defines
1448  *        orientation of other faces.
1449  * \return number of reoriented faces.
1450  */
1451 //================================================================================
1452
1453 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
1454                                   const gp_Vec&       theDirection,
1455                                   TIDSortedElemSet &  theRefFaces,
1456                                   bool                theAllowNonManifold )
1457 {
1458   int nbReori = 0;
1459
1460   if ( theFaces.empty() )
1461   {
1462     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1463     while ( fIt->more() )
1464       theFaces.insert( theFaces.end(), fIt->next() );
1465
1466     if ( theFaces.empty() )
1467       return nbReori;
1468   }
1469
1470   // orient theRefFaces according to theDirection
1471   if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1472     for ( const SMDS_MeshElement* refFace : theRefFaces )
1473     {
1474       gp_XYZ normal;
1475       SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1476       if ( normal * theDirection.XYZ() < 0 )
1477         nbReori += Reorient( refFace );
1478     }
1479
1480   // mark reference faces
1481   GetMeshDS()->SetAllCellsNotMarked();
1482   for ( const SMDS_MeshElement* refFace : theRefFaces )
1483     refFace->setIsMarked( true );
1484
1485   // erase reference faces from theFaces
1486   for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1487     if ( (*fIt)->isMarked() )
1488       fIt = theFaces.erase( fIt );
1489     else
1490       ++fIt;
1491
1492   if ( theRefFaces.empty() )
1493   {
1494     theRefFaces.insert( *theFaces.begin() );
1495     theFaces.erase( theFaces.begin() );
1496   }
1497
1498   // Orient theFaces
1499
1500   // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1501   //   theFaces.erase( theFace );
1502
1503   int nodeInd1, nodeInd2;
1504   const SMDS_MeshElement*           refFace, *otherFace;
1505   vector< const SMDS_MeshElement* > facesNearLink;
1506   vector< std::pair< int, int > >   nodeIndsOfFace;
1507   TIDSortedElemSet                  avoidSet, emptySet;
1508   NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1509
1510   while ( !theRefFaces.empty() )
1511   {
1512     auto refFaceIt = theRefFaces.begin();
1513     refFace = *refFaceIt;
1514     theRefFaces.erase( refFaceIt );
1515
1516     avoidSet.clear();
1517     avoidSet.insert( refFace );
1518
1519     NLink link( refFace->GetNode( 0 ), nullptr );
1520
1521     const int nbNodes = refFace->NbCornerNodes();
1522     for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1523     {
1524       link.second = refFace->GetNode(( i+1 ) % nbNodes );
1525       bool isLinkVisited = checkedLinks.Contains( link );
1526       if ( isLinkVisited )
1527       {
1528         // link has already been checked and won't be encountered more
1529         // if the group (theFaces) is manifold
1530         //checkedLinks.erase( linkIt_isNew.first );
1531       }
1532       else
1533       {
1534         checkedLinks.Add( link );
1535
1536         facesNearLink.clear();
1537         nodeIndsOfFace.clear();
1538         TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1539
1540         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1541                                                              emptySet, avoidSet,
1542                                                              &nodeInd1, &nodeInd2 )))
1543         {
1544           if (( otherFace->isMarked() ) || // ref face
1545               (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1546           {
1547             facesNearLink.push_back( otherFace );
1548             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1549           }
1550           avoidSet.insert( otherFace );
1551         }
1552         if ( facesNearLink.size() > 1 )
1553         {
1554           // NON-MANIFOLD mesh shell !
1555           if ( !theAllowNonManifold )
1556           {
1557             throw SALOME_Exception("Non-manifold topology of groups");
1558           }
1559           // select a face most co-directed with refFace,
1560           // other faces won't be visited this time
1561           gp_XYZ NF, NOF;
1562           SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1563           double proj, maxProj = -1;
1564           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1565           {
1566             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1567             if (( proj = Abs( NF * NOF )) > maxProj )
1568             {
1569               maxProj = proj;
1570               otherFace = facesNearLink[i];
1571               nodeInd1  = nodeIndsOfFace[i].first;
1572               nodeInd2  = nodeIndsOfFace[i].second;
1573             }
1574           }
1575           // not to visit rejected faces
1576           // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1577           //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1578           //     visitedFaces.insert( facesNearLink[i] );
1579         }
1580         else if ( facesNearLink.size() == 1 )
1581         {
1582           otherFace = facesNearLink[0];
1583           nodeInd1  = nodeIndsOfFace.back().first;
1584           nodeInd2  = nodeIndsOfFace.back().second;
1585         }
1586         if ( otherFace )
1587         {
1588           // link must be reverse in otherFace if orientation of otherFace
1589           // is same as that of refFace
1590           if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1591           {
1592             if ( otherFace->isMarked() )
1593               throw SALOME_Exception("Different orientation of reference faces");
1594             nbReori += Reorient( otherFace );
1595           }
1596           if ( !otherFace->isMarked() )
1597           {
1598             theRefFaces.insert( otherFace );
1599             if ( objFaceIt != theFaces.end() )
1600               theFaces.erase( objFaceIt );
1601           }
1602         }
1603       }
1604       link.first = link.second; // reverse the link
1605
1606     } // loop on links of refFace
1607
1608     if ( theRefFaces.empty() && !theFaces.empty() )
1609     {
1610       theRefFaces.insert( *theFaces.begin() );
1611       theFaces.erase( theFaces.begin() );
1612     }
1613
1614   } // while ( !theRefFaces.empty() )
1615
1616   return nbReori;
1617 }
1618
1619 //================================================================================
1620 /*!
1621  * \brief Reorient faces basing on orientation of adjacent volumes.
1622  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1623  * \param theVolumes - reference volumes.
1624  * \param theOutsideNormal - to orient faces to have their normal
1625  *        pointing either \a outside or \a inside the adjacent volumes.
1626  * \return number of reoriented faces.
1627  */
1628 //================================================================================
1629
1630 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1631                                       TIDSortedElemSet & theVolumes,
1632                                       const bool         theOutsideNormal)
1633 {
1634   int nbReori = 0;
1635
1636   SMDS_ElemIteratorPtr faceIt;
1637   if ( theFaces.empty() )
1638     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1639   else
1640     faceIt = SMESHUtils::elemSetIterator( theFaces );
1641
1642   vector< const SMDS_MeshNode* > faceNodes;
1643   TIDSortedElemSet checkedVolumes;
1644   set< const SMDS_MeshNode* > faceNodesSet;
1645   SMDS_VolumeTool volumeTool;
1646
1647   while ( faceIt->more() ) // loop on given faces
1648   {
1649     const SMDS_MeshElement* face = faceIt->next();
1650     if ( face->GetType() != SMDSAbs_Face )
1651       continue;
1652
1653     const size_t nbCornersNodes = face->NbCornerNodes();
1654     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1655
1656     checkedVolumes.clear();
1657     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1658     while ( vIt->more() )
1659     {
1660       const SMDS_MeshElement* volume = vIt->next();
1661
1662       if ( !checkedVolumes.insert( volume ).second )
1663         continue;
1664       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1665         continue;
1666
1667       // is volume adjacent?
1668       bool allNodesCommon = true;
1669       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1670         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1671       if ( !allNodesCommon )
1672         continue;
1673
1674       // get nodes of a corresponding volume facet
1675       faceNodesSet.clear();
1676       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1677       volumeTool.Set( volume );
1678       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1679       if ( facetID < 0 ) continue;
1680       volumeTool.SetExternalNormal();
1681       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1682
1683       // compare order of faceNodes and facetNodes
1684       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1685       int iNN[2];
1686       for ( int i = 0; i < 2; ++i )
1687       {
1688         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1689         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1690           if ( faceNodes[ iN ] == n )
1691           {
1692             iNN[ i ] = iN;
1693             break;
1694           }
1695       }
1696       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1697       if ( isOutside != theOutsideNormal )
1698         nbReori += Reorient( face );
1699     }
1700   }  // loop on given faces
1701
1702   return nbReori;
1703 }
1704
1705 //=======================================================================
1706 //function : getBadRate
1707 //purpose  :
1708 //=======================================================================
1709
1710 static double getBadRate (const SMDS_MeshElement*               theElem,
1711                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1712 {
1713   SMESH::Controls::TSequenceOfXYZ P;
1714   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1715     return 1e100;
1716   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1717   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1718 }
1719
1720 //=======================================================================
1721 //function : QuadToTri
1722 //purpose  : Cut quadrangles into triangles.
1723 //           theCrit is used to select a diagonal to cut
1724 //=======================================================================
1725
1726 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1727                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1728 {
1729   ClearLastCreated();
1730
1731   if ( !theCrit.get() )
1732     return false;
1733
1734   SMESHDS_Mesh *       aMesh = GetMeshDS();
1735   Handle(Geom_Surface) surface;
1736   SMESH_MesherHelper   helper( *GetMesh() );
1737
1738   myLastCreatedElems.reserve( theElems.size() * 2 );
1739
1740   TIDSortedElemSet::iterator itElem;
1741   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1742   {
1743     const SMDS_MeshElement* elem = *itElem;
1744     if ( !elem || elem->GetType() != SMDSAbs_Face )
1745       continue;
1746     if ( elem->NbCornerNodes() != 4 )
1747       continue;
1748
1749     // retrieve element nodes
1750     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1751
1752     // compare two sets of possible triangles
1753     double aBadRate1, aBadRate2; // to what extent a set is bad
1754     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1755     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1756     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1757
1758     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1759     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1760     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1761
1762     const int aShapeId = FindShape( elem );
1763     const SMDS_MeshElement* newElem1 = 0;
1764     const SMDS_MeshElement* newElem2 = 0;
1765
1766     if ( !elem->IsQuadratic() ) // split linear quadrangle
1767     {
1768       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1769       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1770       if ( aBadRate1 <= aBadRate2 ) {
1771         // tr1 + tr2 is better
1772         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1773         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1774       }
1775       else {
1776         // tr3 + tr4 is better
1777         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1778         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1779       }
1780     }
1781     else // split quadratic quadrangle
1782     {
1783       helper.SetIsQuadratic( true );
1784       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1785
1786       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1787       if ( aNodes.size() == 9 )
1788       {
1789         helper.SetIsBiQuadratic( true );
1790         if ( aBadRate1 <= aBadRate2 )
1791           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1792         else
1793           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1794       }
1795       // create a new element
1796       if ( aBadRate1 <= aBadRate2 ) {
1797         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1798         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1799       }
1800       else {
1801         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1802         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1803       }
1804     } // quadratic case
1805
1806     // care of a new element
1807
1808     myLastCreatedElems.push_back(newElem1);
1809     myLastCreatedElems.push_back(newElem2);
1810     AddToSameGroups( newElem1, elem, aMesh );
1811     AddToSameGroups( newElem2, elem, aMesh );
1812
1813     // put a new triangle on the same shape
1814     if ( aShapeId )
1815       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1816     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1817
1818     aMesh->RemoveElement( elem );
1819   }
1820   return true;
1821 }
1822
1823 //=======================================================================
1824 /*!
1825  * \brief Split each of given quadrangles into 4 triangles.
1826  * \param theElems - The faces to be split. If empty all faces are split.
1827  */
1828 //=======================================================================
1829
1830 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1831 {
1832   ClearLastCreated();
1833   myLastCreatedElems.reserve( theElems.size() * 4 );
1834
1835   SMESH_MesherHelper helper( *GetMesh() );
1836   helper.SetElementsOnShape( true );
1837
1838   // get standalone groups of faces
1839   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1840   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1841     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1842       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1843         allFaceGroups.push_back( & group->SMDSGroup() );
1844
1845   bool   checkUV;
1846   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1847   gp_XYZ xyz[9];
1848   vector< const SMDS_MeshNode* > nodes;
1849   SMESHDS_SubMesh*               subMeshDS = 0;
1850   TopoDS_Face                    F;
1851   Handle(Geom_Surface)           surface;
1852   TopLoc_Location                loc;
1853
1854   SMDS_ElemIteratorPtr faceIt;
1855   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1856   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1857
1858   while ( faceIt->more() )
1859   {
1860     const SMDS_MeshElement* quad = faceIt->next();
1861     if ( !quad || quad->NbCornerNodes() != 4 )
1862       continue;
1863
1864     // get a surface the quad is on
1865
1866     if ( quad->getshapeId() < 1 )
1867     {
1868       F.Nullify();
1869       helper.SetSubShape( 0 );
1870       subMeshDS = 0;
1871     }
1872     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1873     {
1874       helper.SetSubShape( quad->getshapeId() );
1875       if ( !helper.GetSubShape().IsNull() &&
1876            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1877       {
1878         F = TopoDS::Face( helper.GetSubShape() );
1879         surface = BRep_Tool::Surface( F, loc );
1880         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1881       }
1882       else
1883       {
1884         helper.SetSubShape( 0 );
1885         subMeshDS = 0;
1886       }
1887     }
1888
1889     // create a central node
1890
1891     const SMDS_MeshNode* nCentral;
1892     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1893
1894     if ( nodes.size() == 9 )
1895     {
1896       nCentral = nodes.back();
1897     }
1898     else
1899     {
1900       size_t iN = 0;
1901       if ( F.IsNull() )
1902       {
1903         for ( ; iN < nodes.size(); ++iN )
1904           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1905
1906         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1907           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1908
1909         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1910                                    xyz[0], xyz[1], xyz[2], xyz[3],
1911                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1912       }
1913       else
1914       {
1915         for ( ; iN < nodes.size(); ++iN )
1916           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1917
1918         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1919           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1920
1921         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1922                                   uv[0], uv[1], uv[2], uv[3],
1923                                   uv[4], uv[5], uv[6], uv[7] );
1924
1925         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1926         xyz[ 8 ] = p.XYZ();
1927       }
1928
1929       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1930                                  uv[8].X(), uv[8].Y() );
1931       myLastCreatedNodes.push_back( nCentral );
1932     }
1933
1934     helper.SetIsQuadratic  ( nodes.size() > 4 );
1935     helper.SetIsBiQuadratic( nodes.size() == 9 );
1936     if ( helper.GetIsQuadratic() )
1937       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1938
1939     // select groups to update
1940     faceGroups.clear();
1941     for ( SMDS_MeshGroup* group : allFaceGroups )
1942       if ( group->Remove( quad ))
1943         faceGroups.push_back( group );
1944
1945     // create 4 triangles
1946
1947     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1948
1949     for ( int i = 0; i < 4; ++i )
1950     {
1951       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1952                                                nodes[(i+1)%4],
1953                                                nCentral );
1954       myLastCreatedElems.push_back( tria );
1955       for ( SMDS_MeshGroup* group : faceGroups )
1956         group->Add( tria );
1957     }
1958   }
1959 }
1960
1961 //=======================================================================
1962 //function : BestSplit
1963 //purpose  : Find better diagonal for cutting.
1964 //=======================================================================
1965
1966 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1967                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1968 {
1969   ClearLastCreated();
1970
1971   if (!theCrit.get())
1972     return -1;
1973
1974   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1975     return -1;
1976
1977   if( theQuad->NbNodes()==4 ||
1978       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1979
1980     // retrieve element nodes
1981     const SMDS_MeshNode* aNodes [4];
1982     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1983     int i = 0;
1984     //while (itN->more())
1985     while (i<4) {
1986       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1987     }
1988     // compare two sets of possible triangles
1989     double aBadRate1, aBadRate2; // to what extent a set is bad
1990     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1991     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1992     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1993
1994     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1995     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1996     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1997     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1998     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1999     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2000       return 1; // diagonal 1-3
2001
2002     return 2; // diagonal 2-4
2003   }
2004   return -1;
2005 }
2006
2007 namespace
2008 {
2009   // Methods of splitting volumes into tetra
2010
2011   const int theHexTo5_1[5*4+1] =
2012     {
2013       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
2014     };
2015   const int theHexTo5_2[5*4+1] =
2016     {
2017       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
2018     };
2019   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2020
2021   const int theHexTo6_1[6*4+1] =
2022     {
2023       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
2024     };
2025   const int theHexTo6_2[6*4+1] =
2026     {
2027       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
2028     };
2029   const int theHexTo6_3[6*4+1] =
2030     {
2031       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
2032     };
2033   const int theHexTo6_4[6*4+1] =
2034     {
2035       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
2036     };
2037   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2038
2039   const int thePyraTo2_1[2*4+1] =
2040     {
2041       0, 1, 2, 4,    0, 2, 3, 4,   -1
2042     };
2043   const int thePyraTo2_2[2*4+1] =
2044     {
2045       1, 2, 3, 4,    1, 3, 0, 4,   -1
2046     };
2047   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2048
2049   const int thePentaTo3_1[3*4+1] =
2050     {
2051       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
2052     };
2053   const int thePentaTo3_2[3*4+1] =
2054     {
2055       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
2056     };
2057   const int thePentaTo3_3[3*4+1] =
2058     {
2059       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
2060     };
2061   const int thePentaTo3_4[3*4+1] =
2062     {
2063       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
2064     };
2065   const int thePentaTo3_5[3*4+1] =
2066     {
2067       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
2068     };
2069   const int thePentaTo3_6[3*4+1] =
2070     {
2071       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
2072     };
2073   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2074                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2075
2076   // Methods of splitting hexahedron into prisms
2077
2078   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2079     {
2080       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
2081     };
2082   const int theHexTo4Prisms_LR[6*4+1] = // left-right
2083     {
2084       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
2085     };
2086   const int theHexTo4Prisms_FB[6*4+1] = // front-back
2087     {
2088       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
2089     };
2090
2091   const int theHexTo2Prisms_BT_1[6*2+1] =
2092     {
2093       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
2094     };
2095   const int theHexTo2Prisms_BT_2[6*2+1] =
2096     {
2097       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
2098     };
2099   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2100
2101   const int theHexTo2Prisms_LR_1[6*2+1] =
2102     {
2103       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2104     };
2105   const int theHexTo2Prisms_LR_2[6*2+1] =
2106     {
2107       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2108     };
2109   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2110
2111   const int theHexTo2Prisms_FB_1[6*2+1] =
2112     {
2113       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
2114     };
2115   const int theHexTo2Prisms_FB_2[6*2+1] =
2116     {
2117       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
2118     };
2119   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2120
2121
2122   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2123   {
2124     int _n1, _n2, _n3;
2125     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2126     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2127     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
2128                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2129   };
2130   struct TSplitMethod
2131   {
2132     int        _nbSplits;
2133     int        _nbCorners;
2134     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2135     bool       _baryNode;     //!< additional node is to be created at cell barycenter
2136     bool       _ownConn;      //!< to delete _connectivity in destructor
2137     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2138
2139     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2140       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2141     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2142     TSplitMethod(const TSplitMethod &splitMethod)
2143       : _nbSplits(splitMethod._nbSplits),
2144         _nbCorners(splitMethod._nbCorners),
2145         _baryNode(splitMethod._baryNode),
2146         _ownConn(splitMethod._ownConn),
2147         _faceBaryNode(splitMethod._faceBaryNode)
2148     {
2149       _connectivity = splitMethod._connectivity;
2150       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2151       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2152     }
2153     bool hasFacet( const TTriangleFacet& facet ) const
2154     {
2155       if ( _nbCorners == 4 )
2156       {
2157         const int* tetConn = _connectivity;
2158         for ( ; tetConn[0] >= 0; tetConn += 4 )
2159           if (( facet.contains( tetConn[0] ) +
2160                 facet.contains( tetConn[1] ) +
2161                 facet.contains( tetConn[2] ) +
2162                 facet.contains( tetConn[3] )) == 3 )
2163             return true;
2164       }
2165       else // prism, _nbCorners == 6
2166       {
2167         const int* prismConn = _connectivity;
2168         for ( ; prismConn[0] >= 0; prismConn += 6 )
2169         {
2170           if (( facet.contains( prismConn[0] ) &&
2171                 facet.contains( prismConn[1] ) &&
2172                 facet.contains( prismConn[2] ))
2173               ||
2174               ( facet.contains( prismConn[3] ) &&
2175                 facet.contains( prismConn[4] ) &&
2176                 facet.contains( prismConn[5] )))
2177             return true;
2178         }
2179       }
2180       return false;
2181     }
2182   };
2183
2184   //=======================================================================
2185   /*!
2186    * \brief return TSplitMethod for the given element to split into tetrahedra
2187    */
2188   //=======================================================================
2189
2190   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2191   {
2192     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2193
2194     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2195     // an edge and a face barycenter; tertaherdons are based on triangles and
2196     // a volume barycenter
2197     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2198
2199     // Find out how adjacent volumes are split
2200
2201     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2202     int hasAdjacentSplits = 0, maxTetConnSize = 0;
2203     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2204     {
2205       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2206       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2207       if ( nbNodes < 4 ) continue;
2208
2209       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2210       const int* nInd = vol.GetFaceNodesIndices( iF );
2211       if ( nbNodes == 4 )
2212       {
2213         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2214         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2215         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2216         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2217       }
2218       else
2219       {
2220         int iCom = 0; // common node of triangle faces to split into
2221         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2222         {
2223           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
2224                                nInd[ iQ * ( (iCom+1)%nbNodes )],
2225                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
2226           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
2227                                nInd[ iQ * ( (iCom+2)%nbNodes )],
2228                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
2229           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2230           {
2231             triaSplits.push_back( t012 );
2232             triaSplits.push_back( t023 );
2233             break;
2234           }
2235         }
2236       }
2237       if ( !triaSplits.empty() )
2238         hasAdjacentSplits = true;
2239     }
2240
2241     // Among variants of split method select one compliant with adjacent volumes
2242
2243     TSplitMethod method;
2244     if ( !vol.Element()->IsPoly() && !is24TetMode )
2245     {
2246       int nbVariants = 2, nbTet = 0;
2247       const int** connVariants = 0;
2248       switch ( vol.Element()->GetEntityType() )
2249       {
2250       case SMDSEntity_Hexa:
2251       case SMDSEntity_Quad_Hexa:
2252       case SMDSEntity_TriQuad_Hexa:
2253         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2254           connVariants = theHexTo5, nbTet = 5;
2255         else
2256           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2257         break;
2258       case SMDSEntity_Pyramid:
2259       case SMDSEntity_Quad_Pyramid:
2260         connVariants = thePyraTo2;  nbTet = 2;
2261         break;
2262       case SMDSEntity_Penta:
2263       case SMDSEntity_Quad_Penta:
2264       case SMDSEntity_BiQuad_Penta:
2265         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2266         break;
2267       default:
2268         nbVariants = 0;
2269       }
2270       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2271       {
2272         // check method compliance with adjacent tetras,
2273         // all found splits must be among facets of tetras described by this method
2274         method = TSplitMethod( nbTet, connVariants[variant] );
2275         if ( hasAdjacentSplits && method._nbSplits > 0 )
2276         {
2277           bool facetCreated = true;
2278           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2279           {
2280             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2281             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2282               facetCreated = method.hasFacet( *facet );
2283           }
2284           if ( !facetCreated )
2285             method = TSplitMethod(0); // incompatible method
2286         }
2287       }
2288     }
2289     if ( method._nbSplits < 1 )
2290     {
2291       // No standard method is applicable, use a generic solution:
2292       // each facet of a volume is split into triangles and
2293       // each of triangles and a volume barycenter form a tetrahedron.
2294
2295       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2296
2297       int* connectivity = new int[ maxTetConnSize + 1 ];
2298       method._connectivity = connectivity;
2299       method._ownConn = true;
2300       method._baryNode = !isHex27; // to create central node or not
2301
2302       int connSize = 0;
2303       int baryCenInd = vol.NbNodes() - int( isHex27 );
2304       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2305       {
2306         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2307         const int*   nInd = vol.GetFaceNodesIndices( iF );
2308         // find common node of triangle facets of tetra to create
2309         int iCommon = 0; // index in linear numeration
2310         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2311         if ( !triaSplits.empty() )
2312         {
2313           // by found facets
2314           const TTriangleFacet* facet = &triaSplits.front();
2315           for ( ; iCommon < nbNodes-1 ; ++iCommon )
2316             if ( facet->contains( nInd[ iQ * iCommon ]) &&
2317                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2318               break;
2319         }
2320         else if ( nbNodes > 3 && !is24TetMode )
2321         {
2322           // find the best method of splitting into triangles by aspect ratio
2323           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2324           map< double, int > badness2iCommon;
2325           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2326           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2327           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2328           {
2329             double badness = 0;
2330             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2331             {
2332               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2333                                       nodes[ iQ*((iLast-1)%nbNodes)],
2334                                       nodes[ iQ*((iLast  )%nbNodes)]);
2335               badness += getBadRate( &tria, aspectRatio );
2336             }
2337             badness2iCommon.insert( make_pair( badness, iCommon ));
2338           }
2339           // use iCommon with lowest badness
2340           iCommon = badness2iCommon.begin()->second;
2341         }
2342         if ( iCommon >= nbNodes )
2343           iCommon = 0; // something wrong
2344
2345         // fill connectivity of tetrahedra based on a current face
2346         int nbTet = nbNodes - 2;
2347         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2348         {
2349           int faceBaryCenInd;
2350           if ( isHex27 )
2351           {
2352             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2353             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2354           }
2355           else
2356           {
2357             method._faceBaryNode[ iF ] = 0;
2358             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2359           }
2360           nbTet = nbNodes;
2361           for ( int i = 0; i < nbTet; ++i )
2362           {
2363             int i1 = i, i2 = (i+1) % nbNodes;
2364             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2365             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2366             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2367             connectivity[ connSize++ ] = faceBaryCenInd;
2368             connectivity[ connSize++ ] = baryCenInd;
2369           }
2370         }
2371         else
2372         {
2373           for ( int i = 0; i < nbTet; ++i )
2374           {
2375             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2376             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2377             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2378             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2379             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2380             connectivity[ connSize++ ] = baryCenInd;
2381           }
2382         }
2383         method._nbSplits += nbTet;
2384
2385       } // loop on volume faces
2386
2387       connectivity[ connSize++ ] = -1;
2388
2389     } // end of generic solution
2390
2391     return method;
2392   }
2393   //=======================================================================
2394   /*!
2395    * \brief return TSplitMethod to split haxhedron into prisms
2396    */
2397   //=======================================================================
2398
2399   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2400                                     const int        methodFlags,
2401                                     const int        facetToSplit)
2402   {
2403     TSplitMethod method;
2404
2405     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2406     // B, T, L, B, R, F
2407     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2408
2409     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2410     {
2411       static TSplitMethod to4methods[4]; // order BT, LR, FB
2412       if ( to4methods[iF]._nbSplits == 0 )
2413       {
2414         switch ( iF ) {
2415         case 0:
2416           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2417           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2418           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2419           break;
2420         case 1:
2421           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2422           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2423           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2424           break;
2425         case 2:
2426           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2427           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2428           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2429           break;
2430         default: return to4methods[3];
2431         }
2432         to4methods[iF]._nbSplits  = 4;
2433         to4methods[iF]._nbCorners = 6;
2434       }
2435       method = to4methods[iF];
2436       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2437       return method;
2438     }
2439     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2440
2441     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2442
2443     const int nbVariants = 2, nbSplits = 2;
2444     const int** connVariants = 0;
2445     switch ( iF ) {
2446     case 0: connVariants = theHexTo2Prisms_BT; break;
2447     case 1: connVariants = theHexTo2Prisms_LR; break;
2448     case 2: connVariants = theHexTo2Prisms_FB; break;
2449     default: return method;
2450     }
2451
2452     // look for prisms adjacent via facetToSplit and an opposite one
2453     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2454     {
2455       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2456       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2457       if ( nbNodes != 4 ) return method;
2458
2459       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2460       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2461       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2462       TTriangleFacet* t;
2463       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2464         t = &t012;
2465       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2466         t = &t123;
2467       else
2468         continue;
2469
2470       // there are adjacent prism
2471       for ( int variant = 0; variant < nbVariants; ++variant )
2472       {
2473         // check method compliance with adjacent prisms,
2474         // the found prism facets must be among facets of prisms described by current method
2475         method._nbSplits     = nbSplits;
2476         method._nbCorners    = 6;
2477         method._connectivity = connVariants[ variant ];
2478         if ( method.hasFacet( *t ))
2479           return method;
2480       }
2481     }
2482
2483     // No adjacent prisms. Select a variant with a best aspect ratio.
2484
2485     double badness[2] = { 0., 0. };
2486     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2487     const SMDS_MeshNode** nodes = vol.GetNodes();
2488     for ( int variant = 0; variant < nbVariants; ++variant )
2489       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2490       {
2491         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2492         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2493
2494         method._connectivity = connVariants[ variant ];
2495         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2496         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2497         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2498
2499         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2500                                 nodes[ t->_n2 ],
2501                                 nodes[ t->_n3 ] );
2502         badness[ variant ] += getBadRate( &tria, aspectRatio );
2503       }
2504     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2505
2506     method._nbSplits     = nbSplits;
2507     method._nbCorners    = 6;
2508     method._connectivity = connVariants[ iBetter ];
2509
2510     return method;
2511   }
2512
2513   //================================================================================
2514   /*!
2515    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2516    */
2517   //================================================================================
2518
2519   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2520                                        const SMDSAbs_GeometryType geom ) const
2521   {
2522     // find the tetrahedron including the three nodes of facet
2523     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2524     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2525     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2526     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2527     while ( volIt1->more() )
2528     {
2529       const SMDS_MeshElement* v = volIt1->next();
2530       if ( v->GetGeomType() != geom )
2531         continue;
2532       const int lastCornerInd = v->NbCornerNodes() - 1;
2533       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2534         continue; // medium node not allowed
2535       const int ind2 = v->GetNodeIndex( n2 );
2536       if ( ind2 < 0 || lastCornerInd < ind2 )
2537         continue;
2538       const int ind3 = v->GetNodeIndex( n3 );
2539       if ( ind3 < 0 || lastCornerInd < ind3 )
2540         continue;
2541       return true;
2542     }
2543     return false;
2544   }
2545
2546   //=======================================================================
2547   /*!
2548    * \brief A key of a face of volume
2549    */
2550   //=======================================================================
2551
2552   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2553   {
2554     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2555     {
2556       TIDSortedNodeSet sortedNodes;
2557       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2558       int nbNodes = vol.NbFaceNodes( iF );
2559       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2560       for ( int i = 0; i < nbNodes; i += iQ )
2561         sortedNodes.insert( fNodes[i] );
2562       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2563       first.first   = (*(n++))->GetID();
2564       first.second  = (*(n++))->GetID();
2565       second.first  = (*(n++))->GetID();
2566       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2567     }
2568   };
2569 } // namespace
2570
2571 //=======================================================================
2572 //function : SplitVolumes
2573 //purpose  : Split volume elements into tetrahedra or prisms.
2574 //           If facet ID < 0, element is split into tetrahedra,
2575 //           else a hexahedron is split into prisms so that the given facet is
2576 //           split into triangles
2577 //=======================================================================
2578
2579 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2580                                      const int            theMethodFlags)
2581 {
2582   SMDS_VolumeTool    volTool;
2583   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2584   fHelper.ToFixNodeParameters( true );
2585
2586   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2587   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2588
2589   SMESH_SequenceOfElemPtr newNodes, newElems;
2590
2591   // map face of volume to it's baricenrtic node
2592   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2593   double bc[3];
2594   vector<const SMDS_MeshElement* > splitVols;
2595
2596   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2597   for ( ; elem2facet != theElems.end(); ++elem2facet )
2598   {
2599     const SMDS_MeshElement* elem = elem2facet->first;
2600     const int       facetToSplit = elem2facet->second;
2601     if ( elem->GetType() != SMDSAbs_Volume )
2602       continue;
2603     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2604     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2605       continue;
2606
2607     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2608
2609     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2610                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2611                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2612     if ( splitMethod._nbSplits < 1 ) continue;
2613
2614     // find submesh to add new tetras to
2615     if ( !subMesh || !subMesh->Contains( elem ))
2616     {
2617       int shapeID = FindShape( elem );
2618       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2619       subMesh = GetMeshDS()->MeshElements( shapeID );
2620     }
2621     int iQ;
2622     if ( elem->IsQuadratic() )
2623     {
2624       iQ = 2;
2625       // add quadratic links to the helper
2626       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2627       {
2628         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2629         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2630         for ( int iN = 0; iN < nbN; iN += iQ )
2631           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2632       }
2633       helper.SetIsQuadratic( true );
2634     }
2635     else
2636     {
2637       iQ = 1;
2638       helper.SetIsQuadratic( false );
2639     }
2640     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2641                                         volTool.GetNodes() + elem->NbNodes() );
2642     helper.SetElementsOnShape( true );
2643     if ( splitMethod._baryNode )
2644     {
2645       // make a node at barycenter
2646       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2647       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2648       nodes.push_back( gcNode );
2649       newNodes.push_back( gcNode );
2650     }
2651     if ( !splitMethod._faceBaryNode.empty() )
2652     {
2653       // make or find baricentric nodes of faces
2654       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2655       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2656       {
2657         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2658           volFace2BaryNode.insert
2659           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2660         if ( !f_n->second )
2661         {
2662           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2663           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2664         }
2665         nodes.push_back( iF_n->second = f_n->second );
2666       }
2667     }
2668
2669     // make new volumes
2670     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2671     const int* volConn = splitMethod._connectivity;
2672     if ( splitMethod._nbCorners == 4 ) // tetra
2673       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2674         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2675                                                                nodes[ volConn[1] ],
2676                                                                nodes[ volConn[2] ],
2677                                                                nodes[ volConn[3] ]));
2678     else // prisms
2679       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2680         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2681                                                                nodes[ volConn[1] ],
2682                                                                nodes[ volConn[2] ],
2683                                                                nodes[ volConn[3] ],
2684                                                                nodes[ volConn[4] ],
2685                                                                nodes[ volConn[5] ]));
2686
2687     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2688
2689     // Split faces on sides of the split volume
2690
2691     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2692     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2693     {
2694       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2695       if ( nbNodes < 4 ) continue;
2696
2697       // find an existing face
2698       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2699                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2700       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2701                                                                        /*noMedium=*/false))
2702       {
2703         // make triangles
2704         helper.SetElementsOnShape( false );
2705         vector< const SMDS_MeshElement* > triangles;
2706
2707         // find submesh to add new triangles in
2708         if ( !fSubMesh || !fSubMesh->Contains( face ))
2709         {
2710           int shapeID = FindShape( face );
2711           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2712         }
2713         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2714         if ( iF_n != splitMethod._faceBaryNode.end() )
2715         {
2716           const SMDS_MeshNode *baryNode = iF_n->second;
2717           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2718           {
2719             const SMDS_MeshNode* n1 = fNodes[iN];
2720             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2721             const SMDS_MeshNode *n3 = baryNode;
2722             if ( !volTool.IsFaceExternal( iF ))
2723               swap( n2, n3 );
2724             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2725           }
2726           if ( fSubMesh ) // update position of the bary node on geometry
2727           {
2728             if ( subMesh )
2729               subMesh->RemoveNode( baryNode );
2730             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2731             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2732             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2733             {
2734               fHelper.SetSubShape( s );
2735               gp_XY uv( 1e100, 1e100 );
2736               double distXYZ[4];
2737               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2738                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2739                    uv.X() < 1e100 )
2740               {
2741                 // node is too far from the surface
2742                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2743                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2744                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2745               }
2746             }
2747           }
2748         }
2749         else
2750         {
2751           // among possible triangles create ones described by split method
2752           const int* nInd = volTool.GetFaceNodesIndices( iF );
2753           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2754           int iCom = 0; // common node of triangle faces to split into
2755           list< TTriangleFacet > facets;
2756           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2757           {
2758             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2759                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2760                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2761             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2762                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2763                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2764             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2765             {
2766               facets.push_back( t012 );
2767               facets.push_back( t023 );
2768               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2769                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2770                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2771                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2772               break;
2773             }
2774           }
2775           list< TTriangleFacet >::iterator facet = facets.begin();
2776           if ( facet == facets.end() )
2777             break;
2778           for ( ; facet != facets.end(); ++facet )
2779           {
2780             if ( !volTool.IsFaceExternal( iF ))
2781               swap( facet->_n2, facet->_n3 );
2782             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2783                                                  volNodes[ facet->_n2 ],
2784                                                  volNodes[ facet->_n3 ]));
2785           }
2786         }
2787         for ( size_t i = 0; i < triangles.size(); ++i )
2788         {
2789           if ( !triangles[ i ]) continue;
2790           if ( fSubMesh )
2791             fSubMesh->AddElement( triangles[ i ]);
2792           newElems.push_back( triangles[ i ]);
2793         }
2794         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2795         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2796
2797       } // while a face based on facet nodes exists
2798     } // loop on volume faces to split them into triangles
2799
2800     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2801
2802     if ( geomType == SMDSEntity_TriQuad_Hexa )
2803     {
2804       // remove medium nodes that could become free
2805       for ( int i = 20; i < volTool.NbNodes(); ++i )
2806         if ( volNodes[i]->NbInverseElements() == 0 )
2807           GetMeshDS()->RemoveNode( volNodes[i] );
2808     }
2809   } // loop on volumes to split
2810
2811   myLastCreatedNodes = newNodes;
2812   myLastCreatedElems = newElems;
2813 }
2814
2815 //=======================================================================
2816 //function : GetHexaFacetsToSplit
2817 //purpose  : For hexahedra that will be split into prisms, finds facets to
2818 //           split into triangles. Only hexahedra adjacent to the one closest
2819 //           to theFacetNormal.Location() are returned.
2820 //param [in,out] theHexas - the hexahedra
2821 //param [in]     theFacetNormal - facet normal
2822 //param [out]    theFacets - the hexahedra and found facet IDs
2823 //=======================================================================
2824
2825 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2826                                              const gp_Ax1&     theFacetNormal,
2827                                              TFacetOfElem &    theFacets)
2828 {
2829 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2830
2831   // Find a hexa closest to the location of theFacetNormal
2832
2833   const SMDS_MeshElement* startHex;
2834   {
2835     // get SMDS_ElemIteratorPtr on theHexas
2836     typedef const SMDS_MeshElement*                                      TValue;
2837     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2838     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2839     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2840     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2841     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2842       ( new TElemSetIter( theHexas.begin(),
2843                           theHexas.end(),
2844                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2845
2846     SMESH_ElementSearcher* searcher =
2847       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2848
2849     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2850
2851     delete searcher;
2852
2853     if ( !startHex )
2854       throw SALOME_Exception( THIS_METHOD "startHex not found");
2855   }
2856
2857   // Select a facet of startHex by theFacetNormal
2858
2859   SMDS_VolumeTool vTool( startHex );
2860   double norm[3], dot, maxDot = 0;
2861   int facetID = -1;
2862   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2863     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2864     {
2865       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2866       if ( dot > maxDot )
2867       {
2868         facetID = iF;
2869         maxDot = dot;
2870       }
2871     }
2872   if ( facetID < 0 )
2873     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2874
2875   // Fill theFacets starting from facetID of startHex
2876
2877   // facets used for searching of volumes adjacent to already treated ones
2878   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2879   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2880   TFacetMap facetsToCheck;
2881
2882   set<const SMDS_MeshNode*> facetNodes;
2883   const SMDS_MeshElement*   curHex;
2884
2885   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2886
2887   while ( startHex )
2888   {
2889     // move in two directions from startHex via facetID
2890     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2891     {
2892       curHex       = startHex;
2893       int curFacet = facetID;
2894       if ( is2nd ) // do not treat startHex twice
2895       {
2896         vTool.Set( curHex );
2897         if ( vTool.IsFreeFace( curFacet, &curHex ))
2898         {
2899           curHex = 0;
2900         }
2901         else
2902         {
2903           vTool.GetFaceNodes( curFacet, facetNodes );
2904           vTool.Set( curHex );
2905           curFacet = vTool.GetFaceIndex( facetNodes );
2906         }
2907       }
2908       while ( curHex )
2909       {
2910         // store a facet to split
2911         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2912         {
2913           theFacets.insert( make_pair( curHex, -1 ));
2914           break;
2915         }
2916         if ( !allHex && !theHexas.count( curHex ))
2917           break;
2918
2919         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2920           theFacets.insert( make_pair( curHex, curFacet ));
2921         if ( !facetIt2isNew.second )
2922           break;
2923
2924         // remember not-to-split facets in facetsToCheck
2925         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2926         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2927         {
2928           if ( iF == curFacet && iF == oppFacet )
2929             continue;
2930           TVolumeFaceKey facetKey ( vTool, iF );
2931           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2932           pair< TFacetMap::iterator, bool > it2isnew =
2933             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2934           if ( !it2isnew.second )
2935             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2936         }
2937         // pass to a volume adjacent via oppFacet
2938         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2939         {
2940           curHex = 0;
2941         }
2942         else
2943         {
2944           // get a new curFacet
2945           vTool.GetFaceNodes( oppFacet, facetNodes );
2946           vTool.Set( curHex );
2947           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2948         }
2949       }
2950     } // move in two directions from startHex via facetID
2951
2952     // Find a new startHex by facetsToCheck
2953
2954     startHex = 0;
2955     facetID  = -1;
2956     TFacetMap::iterator fIt = facetsToCheck.begin();
2957     while ( !startHex && fIt != facetsToCheck.end() )
2958     {
2959       const TElemFacets&  elemFacets = fIt->second;
2960       const SMDS_MeshElement*    hex = elemFacets.first->first;
2961       int                 splitFacet = elemFacets.first->second;
2962       int               lateralFacet = elemFacets.second;
2963       facetsToCheck.erase( fIt );
2964       fIt = facetsToCheck.begin();
2965
2966       vTool.Set( hex );
2967       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2968            curHex->GetGeomType() != SMDSGeom_HEXA )
2969         continue;
2970       if ( !allHex && !theHexas.count( curHex ))
2971         continue;
2972
2973       startHex = curHex;
2974
2975       // find a facet of startHex to split
2976
2977       set<const SMDS_MeshNode*> lateralNodes;
2978       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2979       vTool.GetFaceNodes( splitFacet,   facetNodes );
2980       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2981       vTool.Set( startHex );
2982       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2983
2984       // look for a facet of startHex having common nodes with facetNodes
2985       // but not lateralFacet
2986       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2987       {
2988         if ( iF == lateralFacet )
2989           continue;
2990         int nbCommonNodes = 0;
2991         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2992         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2993           nbCommonNodes += facetNodes.count( nn[ iN ]);
2994
2995         if ( nbCommonNodes >= 2 )
2996         {
2997           facetID = iF;
2998           break;
2999         }
3000       }
3001       if ( facetID < 0 )
3002         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3003     }
3004   } //   while ( startHex )
3005
3006   return;
3007 }
3008
3009 namespace
3010 {
3011   //================================================================================
3012   /*!
3013    * \brief Selects nodes of several elements according to a given interlace
3014    *  \param [in] srcNodes - nodes to select from
3015    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
3016    *  \param [in] interlace - indices of nodes for all elements
3017    *  \param [in] nbElems - nb of elements
3018    *  \param [in] nbNodes - nb of nodes in each element
3019    *  \param [in] mesh - the mesh
3020    *  \param [out] elemQueue - a list to push elements found by the selected nodes
3021    *  \param [in] type - type of elements to look for
3022    */
3023   //================================================================================
3024
3025   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3026                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
3027                     const int*                            interlace,
3028                     const int                             nbElems,
3029                     const int                             nbNodes,
3030                     SMESHDS_Mesh*                         mesh = 0,
3031                     list< const SMDS_MeshElement* >*      elemQueue=0,
3032                     SMDSAbs_ElementType                   type=SMDSAbs_All)
3033   {
3034     for ( int iE = 0; iE < nbElems; ++iE )
3035     {
3036       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3037       const int*                         select = & interlace[iE*nbNodes];
3038       elemNodes.resize( nbNodes );
3039       for ( int iN = 0; iN < nbNodes; ++iN )
3040         elemNodes[iN] = srcNodes[ select[ iN ]];
3041     }
3042     const SMDS_MeshElement* e;
3043     if ( elemQueue )
3044       for ( int iE = 0; iE < nbElems; ++iE )
3045         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3046           elemQueue->push_back( e );
3047   }
3048 }
3049
3050 //=======================================================================
3051 /*
3052  * Split bi-quadratic elements into linear ones without creation of additional nodes
3053  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
3054  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3055  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3056  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
3057  *   will be split in order to keep the mesh conformal.
3058  *  \param elems - elements to split
3059  */
3060 //=======================================================================
3061
3062 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3063 {
3064   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3065   vector<const SMDS_MeshElement* > splitElems;
3066   list< const SMDS_MeshElement* > elemQueue;
3067   list< const SMDS_MeshElement* >::iterator elemIt;
3068
3069   SMESHDS_Mesh * mesh = GetMeshDS();
3070   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3071   int nbElems, nbNodes;
3072
3073   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3074   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3075   {
3076     elemQueue.clear();
3077     elemQueue.push_back( *elemSetIt );
3078     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3079     {
3080       const SMDS_MeshElement* elem = *elemIt;
3081       switch( elem->GetEntityType() )
3082       {
3083       case SMDSEntity_TriQuad_Hexa: // HEX27
3084       {
3085         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3086         nbElems  = nbNodes = 8;
3087         elemType = & hexaType;
3088
3089         // get nodes for new elements
3090         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
3091                                  { 1,9,20,8,    17,22,26,21 },
3092                                  { 2,10,20,9,   18,23,26,22 },
3093                                  { 3,11,20,10,  19,24,26,23 },
3094                                  { 16,21,26,24, 4,12,25,15  },
3095                                  { 17,22,26,21, 5,13,25,12  },
3096                                  { 18,23,26,22, 6,14,25,13  },
3097                                  { 19,24,26,23, 7,15,25,14  }};
3098         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3099
3100         // add boundary faces to elemQueue
3101         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
3102                                  { 4,5,6,7, 12,13,14,15, 25 },
3103                                  { 0,1,5,4, 8,17,12,16,  21 },
3104                                  { 1,2,6,5, 9,18,13,17,  22 },
3105                                  { 2,3,7,6, 10,19,14,18, 23 },
3106                                  { 3,0,4,7, 11,16,15,19, 24 }};
3107         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3108
3109         // add boundary segments to elemQueue
3110         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3111                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3112                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3113         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3114         break;
3115       }
3116       case SMDSEntity_BiQuad_Triangle: // TRIA7
3117       {
3118         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3119         nbElems = 3;
3120         nbNodes = 4;
3121         elemType = & quadType;
3122
3123         // get nodes for new elements
3124         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3125         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3126
3127         // add boundary segments to elemQueue
3128         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3129         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3130         break;
3131       }
3132       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3133       {
3134         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3135         nbElems = 4;
3136         nbNodes = 4;
3137         elemType = & quadType;
3138
3139         // get nodes for new elements
3140         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3141         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3142
3143         // add boundary segments to elemQueue
3144         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3145         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3146         break;
3147       }
3148       case SMDSEntity_Quad_Edge:
3149       {
3150         if ( elemIt == elemQueue.begin() )
3151           continue; // an elem is in theElems
3152         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3153         nbElems = 2;
3154         nbNodes = 2;
3155         elemType = & segType;
3156
3157         // get nodes for new elements
3158         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3159         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3160         break;
3161       }
3162       default: continue;
3163       } // switch( elem->GetEntityType() )
3164
3165       // Create new elements
3166
3167       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3168
3169       splitElems.clear();
3170
3171       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3172       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3173       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3174       //elemType->SetID( -1 );
3175
3176       for ( int iE = 0; iE < nbElems; ++iE )
3177         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3178
3179
3180       ReplaceElemInGroups( elem, splitElems, mesh );
3181
3182       if ( subMesh )
3183         for ( size_t i = 0; i < splitElems.size(); ++i )
3184           subMesh->AddElement( splitElems[i] );
3185     }
3186   }
3187 }
3188
3189 //=======================================================================
3190 //function : AddToSameGroups
3191 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
3192 //=======================================================================
3193
3194 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3195                                         const SMDS_MeshElement* elemInGroups,
3196                                         SMESHDS_Mesh *          aMesh)
3197 {
3198   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3199   if (!groups.empty()) {
3200     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3201     for ( ; grIt != groups.end(); grIt++ ) {
3202       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3203       if ( group && group->Contains( elemInGroups ))
3204         group->SMDSGroup().Add( elemToAdd );
3205     }
3206   }
3207 }
3208
3209
3210 //=======================================================================
3211 //function : RemoveElemFromGroups
3212 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
3213 //=======================================================================
3214 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3215                                              SMESHDS_Mesh *          aMesh)
3216 {
3217   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3218   if (!groups.empty())
3219   {
3220     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3221     for (; GrIt != groups.end(); GrIt++)
3222     {
3223       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3224       if (!grp || grp->IsEmpty()) continue;
3225       grp->SMDSGroup().Remove(removeelem);
3226     }
3227   }
3228 }
3229
3230 //================================================================================
3231 /*!
3232  * \brief Replace elemToRm by elemToAdd in the all groups
3233  */
3234 //================================================================================
3235
3236 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3237                                             const SMDS_MeshElement* elemToAdd,
3238                                             SMESHDS_Mesh *          aMesh)
3239 {
3240   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3241   if (!groups.empty()) {
3242     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3243     for ( ; grIt != groups.end(); grIt++ ) {
3244       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3245       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3246         group->SMDSGroup().Add( elemToAdd );
3247     }
3248   }
3249 }
3250
3251 //================================================================================
3252 /*!
3253  * \brief Replace elemToRm by elemToAdd in the all groups
3254  */
3255 //================================================================================
3256
3257 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
3258                                             const vector<const SMDS_MeshElement*>& elemToAdd,
3259                                             SMESHDS_Mesh *                         aMesh)
3260 {
3261   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3262   if (!groups.empty())
3263   {
3264     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3265     for ( ; grIt != groups.end(); grIt++ ) {
3266       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3267       if ( group && group->SMDSGroup().Remove( elemToRm ) )
3268         for ( size_t i = 0; i < elemToAdd.size(); ++i )
3269           group->SMDSGroup().Add( elemToAdd[ i ] );
3270     }
3271   }
3272 }
3273
3274 //=======================================================================
3275 //function : QuadToTri
3276 //purpose  : Cut quadrangles into triangles.
3277 //           theCrit is used to select a diagonal to cut
3278 //=======================================================================
3279
3280 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3281                                   const bool         the13Diag)
3282 {
3283   ClearLastCreated();
3284   myLastCreatedElems.reserve( theElems.size() * 2 );
3285
3286   SMESHDS_Mesh *       aMesh = GetMeshDS();
3287   Handle(Geom_Surface) surface;
3288   SMESH_MesherHelper   helper( *GetMesh() );
3289
3290   TIDSortedElemSet::iterator itElem;
3291   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3292   {
3293     const SMDS_MeshElement* elem = *itElem;
3294     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3295       continue;
3296
3297     if ( elem->NbNodes() == 4 ) {
3298       // retrieve element nodes
3299       const SMDS_MeshNode* aNodes [4];
3300       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3301       int i = 0;
3302       while ( itN->more() )
3303         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3304
3305       int aShapeId = FindShape( elem );
3306       const SMDS_MeshElement* newElem1 = 0;
3307       const SMDS_MeshElement* newElem2 = 0;
3308       if ( the13Diag ) {
3309         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3310         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3311       }
3312       else {
3313         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3314         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3315       }
3316       myLastCreatedElems.push_back(newElem1);
3317       myLastCreatedElems.push_back(newElem2);
3318       // put a new triangle on the same shape and add to the same groups
3319       if ( aShapeId )
3320       {
3321         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3322         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3323       }
3324       AddToSameGroups( newElem1, elem, aMesh );
3325       AddToSameGroups( newElem2, elem, aMesh );
3326       aMesh->RemoveElement( elem );
3327     }
3328
3329     // Quadratic quadrangle
3330
3331     else if ( elem->NbNodes() >= 8 )
3332     {
3333       // get surface elem is on
3334       int aShapeId = FindShape( elem );
3335       if ( aShapeId != helper.GetSubShapeID() ) {
3336         surface.Nullify();
3337         TopoDS_Shape shape;
3338         if ( aShapeId > 0 )
3339           shape = aMesh->IndexToShape( aShapeId );
3340         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3341           TopoDS_Face face = TopoDS::Face( shape );
3342           surface = BRep_Tool::Surface( face );
3343           if ( !surface.IsNull() )
3344             helper.SetSubShape( shape );
3345         }
3346       }
3347
3348       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3349       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3350       for ( int i = 0; itN->more(); ++i )
3351         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3352
3353       const SMDS_MeshNode* centrNode = aNodes[8];
3354       if ( centrNode == 0 )
3355       {
3356         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3358                                            surface.IsNull() );
3359         myLastCreatedNodes.push_back(centrNode);
3360       }
3361
3362       // create a new element
3363       const SMDS_MeshElement* newElem1 = 0;
3364       const SMDS_MeshElement* newElem2 = 0;
3365       if ( the13Diag ) {
3366         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3367                                   aNodes[6], aNodes[7], centrNode );
3368         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3369                                   centrNode, aNodes[4], aNodes[5] );
3370       }
3371       else {
3372         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3373                                   aNodes[7], aNodes[4], centrNode );
3374         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3375                                   centrNode, aNodes[5], aNodes[6] );
3376       }
3377       myLastCreatedElems.push_back(newElem1);
3378       myLastCreatedElems.push_back(newElem2);
3379       // put a new triangle on the same shape and add to the same groups
3380       if ( aShapeId )
3381       {
3382         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3383         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3384       }
3385       AddToSameGroups( newElem1, elem, aMesh );
3386       AddToSameGroups( newElem2, elem, aMesh );
3387       aMesh->RemoveElement( elem );
3388     }
3389   }
3390
3391   return true;
3392 }
3393
3394 //=======================================================================
3395 //function : getAngle
3396 //purpose  :
3397 //=======================================================================
3398
3399 double getAngle(const SMDS_MeshElement * tr1,
3400                 const SMDS_MeshElement * tr2,
3401                 const SMDS_MeshNode *    n1,
3402                 const SMDS_MeshNode *    n2)
3403 {
3404   double angle = 2. * M_PI; // bad angle
3405
3406   // get normals
3407   SMESH::Controls::TSequenceOfXYZ P1, P2;
3408   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3409        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3410     return angle;
3411   gp_Vec N1,N2;
3412   if(!tr1->IsQuadratic())
3413     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3414   else
3415     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3416   if ( N1.SquareMagnitude() <= gp::Resolution() )
3417     return angle;
3418   if(!tr2->IsQuadratic())
3419     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3420   else
3421     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3422   if ( N2.SquareMagnitude() <= gp::Resolution() )
3423     return angle;
3424
3425   // find the first diagonal node n1 in the triangles:
3426   // take in account a diagonal link orientation
3427   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3428   for ( int t = 0; t < 2; t++ ) {
3429     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3430     int i = 0, iDiag = -1;
3431     while ( it->more()) {
3432       const SMDS_MeshElement *n = it->next();
3433       if ( n == n1 || n == n2 ) {
3434         if ( iDiag < 0)
3435           iDiag = i;
3436         else {
3437           if ( i - iDiag == 1 )
3438             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3439           else
3440             nFirst[ t ] = n;
3441           break;
3442         }
3443       }
3444       i++;
3445     }
3446   }
3447   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3448     N2.Reverse();
3449
3450   angle = N1.Angle( N2 );
3451   //SCRUTE( angle );
3452   return angle;
3453 }
3454
3455 // =================================================
3456 // class generating a unique ID for a pair of nodes
3457 // and able to return nodes by that ID
3458 // =================================================
3459 class LinkID_Gen {
3460 public:
3461
3462   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3463     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3464   {}
3465
3466   smIdType GetLinkID (const SMDS_MeshNode * n1,
3467                   const SMDS_MeshNode * n2) const
3468   {
3469     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3470   }
3471
3472   bool GetNodes (const long             theLinkID,
3473                  const SMDS_MeshNode* & theNode1,
3474                  const SMDS_MeshNode* & theNode2) const
3475   {
3476     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3477     if ( !theNode1 ) return false;
3478     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3479     if ( !theNode2 ) return false;
3480     return true;
3481   }
3482
3483 private:
3484   LinkID_Gen();
3485   const SMESHDS_Mesh* myMesh;
3486   long                myMaxID;
3487 };
3488
3489
3490 //=======================================================================
3491 //function : TriToQuad
3492 //purpose  : Fuse neighbour triangles into quadrangles.
3493 //           theCrit is used to select a neighbour to fuse with.
3494 //           theMaxAngle is a max angle between element normals at which
3495 //           fusion is still performed.
3496 //=======================================================================
3497
3498 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3499                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3500                                   const double                         theMaxAngle)
3501 {
3502   ClearLastCreated();
3503   myLastCreatedElems.reserve( theElems.size() / 2 );
3504
3505   if ( !theCrit.get() )
3506     return false;
3507
3508   SMESHDS_Mesh * aMesh = GetMeshDS();
3509
3510   // Prepare data for algo: build
3511   // 1. map of elements with their linkIDs
3512   // 2. map of linkIDs with their elements
3513
3514   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3515   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3516   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3517   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3518
3519   TIDSortedElemSet::iterator itElem;
3520   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3521   {
3522     const SMDS_MeshElement* elem = *itElem;
3523     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3524     bool IsTria = ( elem->NbCornerNodes()==3 );
3525     if (!IsTria) continue;
3526
3527     // retrieve element nodes
3528     const SMDS_MeshNode* aNodes [4];
3529     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3530     int i = 0;
3531     while ( i < 3 )
3532       aNodes[ i++ ] = itN->next();
3533     aNodes[ 3 ] = aNodes[ 0 ];
3534
3535     // fill maps
3536     for ( i = 0; i < 3; i++ ) {
3537       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3538       // check if elements sharing a link can be fused
3539       itLE = mapLi_listEl.find( link );
3540       if ( itLE != mapLi_listEl.end() ) {
3541         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3542           continue;
3543         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3544         //if ( FindShape( elem ) != FindShape( elem2 ))
3545         //  continue; // do not fuse triangles laying on different shapes
3546         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3547           continue; // avoid making badly shaped quads
3548         (*itLE).second.push_back( elem );
3549       }
3550       else {
3551         mapLi_listEl[ link ].push_back( elem );
3552       }
3553       mapEl_setLi [ elem ].insert( link );
3554     }
3555   }
3556   // Clean the maps from the links shared by a sole element, ie
3557   // links to which only one element is bound in mapLi_listEl
3558
3559   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3560     int nbElems = (*itLE).second.size();
3561     if ( nbElems < 2  ) {
3562       const SMDS_MeshElement* elem = (*itLE).second.front();
3563       SMESH_TLink link = (*itLE).first;
3564       mapEl_setLi[ elem ].erase( link );
3565       if ( mapEl_setLi[ elem ].empty() )
3566         mapEl_setLi.erase( elem );
3567     }
3568   }
3569
3570   // Algo: fuse triangles into quadrangles
3571
3572   while ( ! mapEl_setLi.empty() ) {
3573     // Look for the start element:
3574     // the element having the least nb of shared links
3575     const SMDS_MeshElement* startElem = 0;
3576     int minNbLinks = 4;
3577     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3578       int nbLinks = (*itEL).second.size();
3579       if ( nbLinks < minNbLinks ) {
3580         startElem = (*itEL).first;
3581         minNbLinks = nbLinks;
3582         if ( minNbLinks == 1 )
3583           break;
3584       }
3585     }
3586
3587     // search elements to fuse starting from startElem or links of elements
3588     // fused earlyer - startLinks
3589     list< SMESH_TLink > startLinks;
3590     while ( startElem || !startLinks.empty() ) {
3591       while ( !startElem && !startLinks.empty() ) {
3592         // Get an element to start, by a link
3593         SMESH_TLink linkId = startLinks.front();
3594         startLinks.pop_front();
3595         itLE = mapLi_listEl.find( linkId );
3596         if ( itLE != mapLi_listEl.end() ) {
3597           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3598           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3599           for ( ; itE != listElem.end() ; itE++ )
3600             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3601               startElem = (*itE);
3602           mapLi_listEl.erase( itLE );
3603         }
3604       }
3605
3606       if ( startElem ) {
3607         // Get candidates to be fused
3608         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3609         const SMESH_TLink *link12 = 0, *link13 = 0;
3610         startElem = 0;
3611         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3612         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3613         ASSERT( !setLi.empty() );
3614         set< SMESH_TLink >::iterator itLi;
3615         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3616         {
3617           const SMESH_TLink & link = (*itLi);
3618           itLE = mapLi_listEl.find( link );
3619           if ( itLE == mapLi_listEl.end() )
3620             continue;
3621
3622           const SMDS_MeshElement* elem = (*itLE).second.front();
3623           if ( elem == tr1 )
3624             elem = (*itLE).second.back();
3625           mapLi_listEl.erase( itLE );
3626           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3627             continue;
3628           if ( tr2 ) {
3629             tr3 = elem;
3630             link13 = &link;
3631           }
3632           else {
3633             tr2 = elem;
3634             link12 = &link;
3635           }
3636
3637           // add other links of elem to list of links to re-start from
3638           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3639           set< SMESH_TLink >::iterator it;
3640           for ( it = links.begin(); it != links.end(); it++ ) {
3641             const SMESH_TLink& link2 = (*it);
3642             if ( link2 != link )
3643               startLinks.push_back( link2 );
3644           }
3645         }
3646
3647         // Get nodes of possible quadrangles
3648         const SMDS_MeshNode *n12 [4], *n13 [4];
3649         bool Ok12 = false, Ok13 = false;
3650         const SMDS_MeshNode *linkNode1, *linkNode2;
3651         if(tr2) {
3652           linkNode1 = link12->first;
3653           linkNode2 = link12->second;
3654           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3655             Ok12 = true;
3656         }
3657         if(tr3) {
3658           linkNode1 = link13->first;
3659           linkNode2 = link13->second;
3660           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3661             Ok13 = true;
3662         }
3663
3664         // Choose a pair to fuse
3665         if ( Ok12 && Ok13 ) {
3666           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3667           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3668           double aBadRate12 = getBadRate( &quad12, theCrit );
3669           double aBadRate13 = getBadRate( &quad13, theCrit );
3670           if (  aBadRate13 < aBadRate12 )
3671             Ok12 = false;
3672           else
3673             Ok13 = false;
3674         }
3675
3676         // Make quadrangles
3677         // and remove fused elems and remove links from the maps
3678         mapEl_setLi.erase( tr1 );
3679         if ( Ok12 )
3680         {
3681           mapEl_setLi.erase( tr2 );
3682           mapLi_listEl.erase( *link12 );
3683           if ( tr1->NbNodes() == 3 )
3684           {
3685             const SMDS_MeshElement* newElem = 0;
3686             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3687             myLastCreatedElems.push_back(newElem);
3688             AddToSameGroups( newElem, tr1, aMesh );
3689             int aShapeId = tr1->getshapeId();
3690             if ( aShapeId )
3691               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3692             aMesh->RemoveElement( tr1 );
3693             aMesh->RemoveElement( tr2 );
3694           }
3695           else {
3696             vector< const SMDS_MeshNode* > N1;
3697             vector< const SMDS_MeshNode* > N2;
3698             getNodesFromTwoTria(tr1,tr2,N1,N2);
3699             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3700             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3701             // i.e. first nodes from both arrays form a new diagonal
3702             const SMDS_MeshNode* aNodes[8];
3703             aNodes[0] = N1[0];
3704             aNodes[1] = N1[1];
3705             aNodes[2] = N2[0];
3706             aNodes[3] = N2[1];
3707             aNodes[4] = N1[3];
3708             aNodes[5] = N2[5];
3709             aNodes[6] = N2[3];
3710             aNodes[7] = N1[5];
3711             const SMDS_MeshElement* newElem = 0;
3712             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3713               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3714                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3715             else
3716               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3718             myLastCreatedElems.push_back(newElem);
3719             AddToSameGroups( newElem, tr1, aMesh );
3720             int aShapeId = tr1->getshapeId();
3721             if ( aShapeId )
3722               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3723             aMesh->RemoveElement( tr1 );
3724             aMesh->RemoveElement( tr2 );
3725             // remove middle node (9)
3726             if ( N1[4]->NbInverseElements() == 0 )
3727               aMesh->RemoveNode( N1[4] );
3728             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3729               aMesh->RemoveNode( N1[6] );
3730             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3731               aMesh->RemoveNode( N2[6] );
3732           }
3733         }
3734         else if ( Ok13 )
3735         {
3736           mapEl_setLi.erase( tr3 );
3737           mapLi_listEl.erase( *link13 );
3738           if ( tr1->NbNodes() == 3 ) {
3739             const SMDS_MeshElement* newElem = 0;
3740             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3741             myLastCreatedElems.push_back(newElem);
3742             AddToSameGroups( newElem, tr1, aMesh );
3743             int aShapeId = tr1->getshapeId();
3744             if ( aShapeId )
3745               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3746             aMesh->RemoveElement( tr1 );
3747             aMesh->RemoveElement( tr3 );
3748           }
3749           else {
3750             vector< const SMDS_MeshNode* > N1;
3751             vector< const SMDS_MeshNode* > N2;
3752             getNodesFromTwoTria(tr1,tr3,N1,N2);
3753             // now we receive following N1 and N2 (using numeration as above image)
3754             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3755             // i.e. first nodes from both arrays form a new diagonal
3756             const SMDS_MeshNode* aNodes[8];
3757             aNodes[0] = N1[0];
3758             aNodes[1] = N1[1];
3759             aNodes[2] = N2[0];
3760             aNodes[3] = N2[1];
3761             aNodes[4] = N1[3];
3762             aNodes[5] = N2[5];
3763             aNodes[6] = N2[3];
3764             aNodes[7] = N1[5];
3765             const SMDS_MeshElement* newElem = 0;
3766             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3767               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3768                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3769             else
3770               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3772             myLastCreatedElems.push_back(newElem);
3773             AddToSameGroups( newElem, tr1, aMesh );
3774             int aShapeId = tr1->getshapeId();
3775             if ( aShapeId )
3776               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3777             aMesh->RemoveElement( tr1 );
3778             aMesh->RemoveElement( tr3 );
3779             // remove middle node (9)
3780             if ( N1[4]->NbInverseElements() == 0 )
3781               aMesh->RemoveNode( N1[4] );
3782             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3783               aMesh->RemoveNode( N1[6] );
3784             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3785               aMesh->RemoveNode( N2[6] );
3786           }
3787         }
3788
3789         // Next element to fuse: the rejected one
3790         if ( tr3 )
3791           startElem = Ok12 ? tr3 : tr2;
3792
3793       } // if ( startElem )
3794     } // while ( startElem || !startLinks.empty() )
3795   } // while ( ! mapEl_setLi.empty() )
3796
3797   return true;
3798 }
3799
3800 //================================================================================
3801 /*!
3802  * \brief Return nodes linked to the given one
3803  * \param theNode - the node
3804  * \param linkedNodes - the found nodes
3805  * \param type - the type of elements to check
3806  *
3807  * Medium nodes are ignored
3808  */
3809 //================================================================================
3810
3811 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3812                                        TIDSortedElemSet &   linkedNodes,
3813                                        SMDSAbs_ElementType  type )
3814 {
3815   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3816   while ( elemIt->more() )
3817   {
3818     const SMDS_MeshElement* elem = elemIt->next();
3819     if(elem->GetType() == SMDSAbs_0DElement)
3820       continue;
3821
3822     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3823     if ( elem->GetType() == SMDSAbs_Volume )
3824     {
3825       SMDS_VolumeTool vol( elem );
3826       while ( nodeIt->more() ) {
3827         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3828         if ( theNode != n && vol.IsLinked( theNode, n ))
3829           linkedNodes.insert( n );
3830       }
3831     }
3832     else
3833     {
3834       for ( int i = 0; nodeIt->more(); ++i ) {
3835         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3836         if ( n == theNode ) {
3837           int iBefore = i - 1;
3838           int iAfter  = i + 1;
3839           if ( elem->IsQuadratic() ) {
3840             int nb = elem->NbNodes() / 2;
3841             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3842             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3843           }
3844           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3845           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3846         }
3847       }
3848     }
3849   }
3850 }
3851
3852 //=======================================================================
3853 //function : laplacianSmooth
3854 //purpose  : pulls theNode toward the center of surrounding nodes directly
3855 //           connected to that node along an element edge
3856 //=======================================================================
3857
3858 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3859                      const Handle(Geom_Surface)&          theSurface,
3860                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3861 {
3862   // find surrounding nodes
3863
3864   TIDSortedElemSet nodeSet;
3865   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3866
3867   // compute new coodrs
3868
3869   double coord[] = { 0., 0., 0. };
3870   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3871   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3872     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3873     if ( theSurface.IsNull() ) { // smooth in 3D
3874       coord[0] += node->X();
3875       coord[1] += node->Y();
3876       coord[2] += node->Z();
3877     }
3878     else { // smooth in 2D
3879       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3880       gp_XY* uv = theUVMap[ node ];
3881       coord[0] += uv->X();
3882       coord[1] += uv->Y();
3883     }
3884   }
3885   int nbNodes = nodeSet.size();
3886   if ( !nbNodes )
3887     return;
3888   coord[0] /= nbNodes;
3889   coord[1] /= nbNodes;
3890
3891   if ( !theSurface.IsNull() ) {
3892     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3893     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3894     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3895     coord[0] = p3d.X();
3896     coord[1] = p3d.Y();
3897     coord[2] = p3d.Z();
3898   }
3899   else
3900     coord[2] /= nbNodes;
3901
3902   // move node
3903
3904   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3905 }
3906
3907 //=======================================================================
3908 //function : centroidalSmooth
3909 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3910 //           surrounding elements
3911 //=======================================================================
3912
3913 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3914                       const Handle(Geom_Surface)&          theSurface,
3915                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3916 {
3917   gp_XYZ aNewXYZ(0.,0.,0.);
3918   SMESH::Controls::Area anAreaFunc;
3919   double totalArea = 0.;
3920   int nbElems = 0;
3921
3922   // compute new XYZ
3923
3924   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3925   while ( elemIt->more() )
3926   {
3927     const SMDS_MeshElement* elem = elemIt->next();
3928     nbElems++;
3929
3930     gp_XYZ elemCenter(0.,0.,0.);
3931     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3932     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3933     int nn = elem->NbNodes();
3934     if(elem->IsQuadratic()) nn = nn/2;
3935     int i=0;
3936     //while ( itN->more() ) {
3937     while ( i<nn ) {
3938       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3939       i++;
3940       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3941       aNodePoints.push_back( aP );
3942       if ( !theSurface.IsNull() ) { // smooth in 2D
3943         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3944         gp_XY* uv = theUVMap[ aNode ];
3945         aP.SetCoord( uv->X(), uv->Y(), 0. );
3946       }
3947       elemCenter += aP;
3948     }
3949     double elemArea = anAreaFunc.GetValue( aNodePoints );
3950     totalArea += elemArea;
3951     elemCenter /= nn;
3952     aNewXYZ += elemCenter * elemArea;
3953   }
3954   aNewXYZ /= totalArea;
3955   if ( !theSurface.IsNull() ) {
3956     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3957     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3958   }
3959
3960   // move node
3961
3962   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3963 }
3964
3965 //=======================================================================
3966 //function : getClosestUV
3967 //purpose  : return UV of closest projection
3968 //=======================================================================
3969
3970 static bool getClosestUV (Extrema_GenExtPS& projector,
3971                           const gp_Pnt&     point,
3972                           gp_XY &           result)
3973 {
3974   projector.Perform( point );
3975   if ( projector.IsDone() ) {
3976     double u = 0, v = 0, minVal = DBL_MAX;
3977     for ( int i = projector.NbExt(); i > 0; i-- )
3978       if ( projector.SquareDistance( i ) < minVal ) {
3979         minVal = projector.SquareDistance( i );
3980         projector.Point( i ).Parameter( u, v );
3981       }
3982     result.SetCoord( u, v );
3983     return true;
3984   }
3985   return false;
3986 }
3987
3988 //=======================================================================
3989 //function : Smooth
3990 //purpose  : Smooth theElements during theNbIterations or until a worst
3991 //           element has aspect ratio <= theTgtAspectRatio.
3992 //           Aspect Ratio varies in range [1.0, inf].
3993 //           If theElements is empty, the whole mesh is smoothed.
3994 //           theFixedNodes contains additionally fixed nodes. Nodes built
3995 //           on edges and boundary nodes are always fixed.
3996 //=======================================================================
3997
3998 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3999                                set<const SMDS_MeshNode*> & theFixedNodes,
4000                                const SmoothMethod          theSmoothMethod,
4001                                const int                   theNbIterations,
4002                                double                      theTgtAspectRatio,
4003                                const bool                  the2D)
4004 {
4005   ClearLastCreated();
4006
4007   if ( theTgtAspectRatio < 1.0 )
4008     theTgtAspectRatio = 1.0;
4009
4010   const double disttol = 1.e-16;
4011
4012   SMESH::Controls::AspectRatio aQualityFunc;
4013
4014   SMESHDS_Mesh* aMesh = GetMeshDS();
4015
4016   if ( theElems.empty() ) {
4017     // add all faces to theElems
4018     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4019     while ( fIt->more() ) {
4020       const SMDS_MeshElement* face = fIt->next();
4021       theElems.insert( theElems.end(), face );
4022     }
4023   }
4024   // get all face ids theElems are on
4025   set< int > faceIdSet;
4026   TIDSortedElemSet::iterator itElem;
4027   if ( the2D )
4028     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4029       int fId = FindShape( *itElem );
4030       // check that corresponding submesh exists and a shape is face
4031       if (fId &&
4032           faceIdSet.find( fId ) == faceIdSet.end() &&
4033           aMesh->MeshElements( fId )) {
4034         TopoDS_Shape F = aMesh->IndexToShape( fId );
4035         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4036           faceIdSet.insert( fId );
4037       }
4038     }
4039   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4040
4041   // ===============================================
4042   // smooth elements on each TopoDS_Face separately
4043   // ===============================================
4044
4045   SMESH_MesherHelper helper( *GetMesh() );
4046
4047   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4048   for ( ; fId != faceIdSet.rend(); ++fId )
4049   {
4050     // get face surface and submesh
4051     Handle(Geom_Surface) surface;
4052     SMESHDS_SubMesh* faceSubMesh = 0;
4053     TopoDS_Face face;
4054     double fToler2 = 0;
4055     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4056     bool isUPeriodic = false, isVPeriodic = false;
4057     if ( *fId )
4058     {
4059       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4060       surface = BRep_Tool::Surface( face );
4061       faceSubMesh = aMesh->MeshElements( *fId );
4062       fToler2 = BRep_Tool::Tolerance( face );
4063       fToler2 *= fToler2 * 10.;
4064       isUPeriodic = surface->IsUPeriodic();
4065       // if ( isUPeriodic )
4066       //   surface->UPeriod();
4067       isVPeriodic = surface->IsVPeriodic();
4068       // if ( isVPeriodic )
4069       //   surface->VPeriod();
4070       surface->Bounds( u1, u2, v1, v2 );
4071       helper.SetSubShape( face );
4072     }
4073     // ---------------------------------------------------------
4074     // for elements on a face, find movable and fixed nodes and
4075     // compute UV for them
4076     // ---------------------------------------------------------
4077     bool checkBoundaryNodes = false;
4078     bool isQuadratic = false;
4079     set<const SMDS_MeshNode*> setMovableNodes;
4080     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4081     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4082     list< const SMDS_MeshElement* > elemsOnFace;
4083
4084     Extrema_GenExtPS projector;
4085     GeomAdaptor_Surface surfAdaptor;
4086     if ( !surface.IsNull() ) {
4087       surfAdaptor.Load( surface );
4088       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4089     }
4090     int nbElemOnFace = 0;
4091     itElem = theElems.begin();
4092     // loop on not yet smoothed elements: look for elems on a face
4093     while ( itElem != theElems.end() )
4094     {
4095       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4096         break; // all elements found
4097
4098       const SMDS_MeshElement* elem = *itElem;
4099       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4100            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4101         ++itElem;
4102         continue;
4103       }
4104       elemsOnFace.push_back( elem );
4105       theElems.erase( itElem++ );
4106       nbElemOnFace++;
4107
4108       if ( !isQuadratic )
4109         isQuadratic = elem->IsQuadratic();
4110
4111       // get movable nodes of elem
4112       const SMDS_MeshNode* node;
4113       SMDS_TypeOfPosition posType;
4114       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4115       int nn = 0, nbn =  elem->NbNodes();
4116       if(elem->IsQuadratic())
4117         nbn = nbn/2;
4118       while ( nn++ < nbn ) {
4119         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4120         const SMDS_PositionPtr& pos = node->GetPosition();
4121         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122         if (posType != SMDS_TOP_EDGE &&
4123             posType != SMDS_TOP_VERTEX &&
4124             theFixedNodes.find( node ) == theFixedNodes.end())
4125         {
4126           // check if all faces around the node are on faceSubMesh
4127           // because a node on edge may be bound to face
4128           bool all = true;
4129           if ( faceSubMesh ) {
4130             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4131             while ( eIt->more() && all ) {
4132               const SMDS_MeshElement* e = eIt->next();
4133               all = faceSubMesh->Contains( e );
4134             }
4135           }
4136           if ( all )
4137             setMovableNodes.insert( node );
4138           else
4139             checkBoundaryNodes = true;
4140         }
4141         if ( posType == SMDS_TOP_3DSPACE )
4142           checkBoundaryNodes = true;
4143       }
4144
4145       if ( surface.IsNull() )
4146         continue;
4147
4148       // get nodes to check UV
4149       list< const SMDS_MeshNode* > uvCheckNodes;
4150       const SMDS_MeshNode* nodeInFace = 0;
4151       itN = elem->nodesIterator();
4152       nn = 0; nbn =  elem->NbNodes();
4153       if(elem->IsQuadratic())
4154         nbn = nbn/2;
4155       while ( nn++ < nbn ) {
4156         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4157         if ( node->GetPosition()->GetDim() == 2 )
4158           nodeInFace = node;
4159         if ( uvMap.find( node ) == uvMap.end() )
4160           uvCheckNodes.push_back( node );
4161         // add nodes of elems sharing node
4162         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4163         //         while ( eIt->more() ) {
4164         //           const SMDS_MeshElement* e = eIt->next();
4165         //           if ( e != elem ) {
4166         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4167         //             while ( nIt->more() ) {
4168         //               const SMDS_MeshNode* n =
4169         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4170         //               if ( uvMap.find( n ) == uvMap.end() )
4171         //                 uvCheckNodes.push_back( n );
4172         //             }
4173         //           }
4174         //         }
4175       }
4176       // check UV on face
4177       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4178       for ( ; n != uvCheckNodes.end(); ++n ) {
4179         node = *n;
4180         gp_XY uv( 0, 0 );
4181         const SMDS_PositionPtr& pos = node->GetPosition();
4182         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4183         // get existing UV
4184         if ( pos )
4185         {
4186           bool toCheck = true;
4187           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4188         }
4189         // compute not existing UV
4190         bool project = ( posType == SMDS_TOP_3DSPACE );
4191         // double dist1 = DBL_MAX, dist2 = 0;
4192         // if ( posType != SMDS_TOP_3DSPACE ) {
4193         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4194         //   project = dist1 > fToler2;
4195         // }
4196         if ( project ) { // compute new UV
4197           gp_XY newUV;
4198           gp_Pnt pNode = SMESH_NodeXYZ( node );
4199           if ( !getClosestUV( projector, pNode, newUV )) {
4200             MESSAGE("Node Projection Failed " << node);
4201           }
4202           else {
4203             if ( isUPeriodic )
4204               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4205             if ( isVPeriodic )
4206               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4207             // check new UV
4208             // if ( posType != SMDS_TOP_3DSPACE )
4209             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4210             // if ( dist2 < dist1 )
4211             uv = newUV;
4212           }
4213         }
4214         // store UV in the map
4215         listUV.push_back( uv );
4216         uvMap.insert( make_pair( node, &listUV.back() ));
4217       }
4218     } // loop on not yet smoothed elements
4219
4220     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4221       checkBoundaryNodes = true;
4222
4223     // fix nodes on mesh boundary
4224
4225     if ( checkBoundaryNodes ) {
4226       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4227       map< SMESH_TLink, int >::iterator link_nb;
4228       // put all elements links to linkNbMap
4229       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4230       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4231         const SMDS_MeshElement* elem = (*elemIt);
4232         int nbn =  elem->NbCornerNodes();
4233         // loop on elem links: insert them in linkNbMap
4234         for ( int iN = 0; iN < nbn; ++iN ) {
4235           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4236           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4237           SMESH_TLink link( n1, n2 );
4238           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4239           link_nb->second++;
4240         }
4241       }
4242       // remove nodes that are in links encountered only once from setMovableNodes
4243       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4244         if ( link_nb->second == 1 ) {
4245           setMovableNodes.erase( link_nb->first.node1() );
4246           setMovableNodes.erase( link_nb->first.node2() );
4247         }
4248       }
4249     }
4250
4251     // -----------------------------------------------------
4252     // for nodes on seam edge, compute one more UV ( uvMap2 );
4253     // find movable nodes linked to nodes on seam and which
4254     // are to be smoothed using the second UV ( uvMap2 )
4255     // -----------------------------------------------------
4256
4257     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4258     if ( !surface.IsNull() ) {
4259       TopExp_Explorer eExp( face, TopAbs_EDGE );
4260       for ( ; eExp.More(); eExp.Next() ) {
4261         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4262         if ( !BRep_Tool::IsClosed( edge, face ))
4263           continue;
4264         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4265         if ( !sm ) continue;
4266         // find out which parameter varies for a node on seam
4267         double f,l;
4268         gp_Pnt2d uv1, uv2;
4269         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4270         if ( pcurve.IsNull() ) continue;
4271         uv1 = pcurve->Value( f );
4272         edge.Reverse();
4273         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4274         if ( pcurve.IsNull() ) continue;
4275         uv2 = pcurve->Value( f );
4276         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4277         // assure uv1 < uv2
4278         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4279           std::swap( uv1, uv2 );
4280         // get nodes on seam and its vertices
4281         list< const SMDS_MeshNode* > seamNodes;
4282         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4283         while ( nSeamIt->more() ) {
4284           const SMDS_MeshNode* node = nSeamIt->next();
4285           if ( !isQuadratic || !IsMedium( node ))
4286             seamNodes.push_back( node );
4287         }
4288         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4289         for ( ; vExp.More(); vExp.Next() ) {
4290           sm = aMesh->MeshElements( vExp.Current() );
4291           if ( sm ) {
4292             nSeamIt = sm->GetNodes();
4293             while ( nSeamIt->more() )
4294               seamNodes.push_back( nSeamIt->next() );
4295           }
4296         }
4297         // loop on nodes on seam
4298         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4299         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4300           const SMDS_MeshNode* nSeam = *noSeIt;
4301           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4302           if ( n_uv == uvMap.end() )
4303             continue;
4304           // set the first UV
4305           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4306           // set the second UV
4307           listUV.push_back( *n_uv->second );
4308           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4309           if ( uvMap2.empty() )
4310             uvMap2 = uvMap; // copy the uvMap contents
4311           uvMap2[ nSeam ] = &listUV.back();
4312
4313           // collect movable nodes linked to ones on seam in nodesNearSeam
4314           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4315           while ( eIt->more() ) {
4316             const SMDS_MeshElement* e = eIt->next();
4317             int nbUseMap1 = 0, nbUseMap2 = 0;
4318             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4319             int nn = 0, nbn =  e->NbNodes();
4320             if(e->IsQuadratic()) nbn = nbn/2;
4321             while ( nn++ < nbn )
4322             {
4323               const SMDS_MeshNode* n =
4324                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4325               if (n == nSeam ||
4326                   setMovableNodes.find( n ) == setMovableNodes.end() )
4327                 continue;
4328               // add only nodes being closer to uv2 than to uv1
4329               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4330               //              0.5 * ( n->Y() + nSeam->Y() ),
4331               //              0.5 * ( n->Z() + nSeam->Z() ));
4332               // gp_XY uv;
4333               // getClosestUV( projector, pMid, uv );
4334               double x = uvMap[ n ]->Coord( iPar );
4335               if ( Abs( uv1.Coord( iPar ) - x ) >
4336                    Abs( uv2.Coord( iPar ) - x )) {
4337                 nodesNearSeam.insert( n );
4338                 nbUseMap2++;
4339               }
4340               else
4341                 nbUseMap1++;
4342             }
4343             // for centroidalSmooth all element nodes must
4344             // be on one side of a seam
4345             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4346               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4347               nn = 0;
4348               while ( nn++ < nbn ) {
4349                 const SMDS_MeshNode* n =
4350                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4351                 setMovableNodes.erase( n );
4352               }
4353             }
4354           }
4355         } // loop on nodes on seam
4356       } // loop on edge of a face
4357     } // if ( !face.IsNull() )
4358
4359     if ( setMovableNodes.empty() ) {
4360       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4361       continue; // goto next face
4362     }
4363
4364     // -------------
4365     // SMOOTHING //
4366     // -------------
4367
4368     int it = -1;
4369     double maxRatio = -1., maxDisplacement = -1.;
4370     set<const SMDS_MeshNode*>::iterator nodeToMove;
4371     for ( it = 0; it < theNbIterations; it++ ) {
4372       maxDisplacement = 0.;
4373       nodeToMove = setMovableNodes.begin();
4374       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4375         const SMDS_MeshNode* node = (*nodeToMove);
4376         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4377
4378         // smooth
4379         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4380         if ( theSmoothMethod == LAPLACIAN )
4381           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4382         else
4383           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4384
4385         // node displacement
4386         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4387         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4388         if ( aDispl > maxDisplacement )
4389           maxDisplacement = aDispl;
4390       }
4391       // no node movement => exit
4392       //if ( maxDisplacement < 1.e-16 ) {
4393       if ( maxDisplacement < disttol ) {
4394         MESSAGE("-- no node movement --");
4395         break;
4396       }
4397
4398       // check elements quality
4399       maxRatio  = 0;
4400       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4401       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4402         const SMDS_MeshElement* elem = (*elemIt);
4403         if ( !elem || elem->GetType() != SMDSAbs_Face )
4404           continue;
4405         SMESH::Controls::TSequenceOfXYZ aPoints;
4406         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4407           double aValue = aQualityFunc.GetValue( aPoints );
4408           if ( aValue > maxRatio )
4409             maxRatio = aValue;
4410         }
4411       }
4412       if ( maxRatio <= theTgtAspectRatio ) {
4413         //MESSAGE("-- quality achieved --");
4414         break;
4415       }
4416       if (it+1 == theNbIterations) {
4417         //MESSAGE("-- Iteration limit exceeded --");
4418       }
4419     } // smoothing iterations
4420
4421     // MESSAGE(" Face id: " << *fId <<
4422     //         " Nb iterstions: " << it <<
4423     //         " Displacement: " << maxDisplacement <<
4424     //         " Aspect Ratio " << maxRatio);
4425
4426     // ---------------------------------------
4427     // new nodes positions are computed,
4428     // record movement in DS and set new UV
4429     // ---------------------------------------
4430     nodeToMove = setMovableNodes.begin();
4431     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4432       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4433       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4434       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4435       if ( node_uv != uvMap.end() ) {
4436         gp_XY* uv = node_uv->second;
4437         node->SetPosition
4438           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4439       }
4440     }
4441
4442     // move medium nodes of quadratic elements
4443     if ( isQuadratic )
4444     {
4445       vector<const SMDS_MeshNode*> nodes;
4446       bool checkUV;
4447       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4448       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4449       {
4450         const SMDS_MeshElement* QF = *elemIt;
4451         if ( QF->IsQuadratic() )
4452         {
4453           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4454                         SMDS_MeshElement::iterator() );
4455           nodes.push_back( nodes[0] );
4456           gp_Pnt xyz;
4457           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4458           {
4459             if ( !surface.IsNull() )
4460             {
4461               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4462               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4463               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4464               xyz = surface->Value( uv.X(), uv.Y() );
4465             }
4466             else {
4467               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4468             }
4469             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4470               // we have to move a medium node
4471               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4472           }
4473         }
4474       }
4475     }
4476
4477   } // loop on face ids
4478
4479 }
4480
4481 namespace
4482 {
4483   //=======================================================================
4484   //function : isReverse
4485   //purpose  : Return true if normal of prevNodes is not co-directied with
4486   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4487   //           iNotSame is where prevNodes and nextNodes are different.
4488   //           If result is true then future volume orientation is OK
4489   //=======================================================================
4490
4491   bool isReverse(const SMDS_MeshElement*             face,
4492                  const vector<const SMDS_MeshNode*>& prevNodes,
4493                  const vector<const SMDS_MeshNode*>& nextNodes,
4494                  const int                           iNotSame)
4495   {
4496
4497     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4498     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4499     gp_XYZ extrDir( pN - pP ), faceNorm;
4500     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4501
4502     return faceNorm * extrDir < 0.0;
4503   }
4504
4505   //================================================================================
4506   /*!
4507    * \brief Assure that theElemSets[0] holds elements, not nodes
4508    */
4509   //================================================================================
4510
4511   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4512   {
4513     if ( !theElemSets[0].empty() &&
4514          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4515     {
4516       std::swap( theElemSets[0], theElemSets[1] );
4517     }
4518     else if ( !theElemSets[1].empty() &&
4519               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4520     {
4521       std::swap( theElemSets[0], theElemSets[1] );
4522     }
4523   }
4524 }
4525
4526 //=======================================================================
4527 /*!
4528  * \brief Create elements by sweeping an element
4529  * \param elem - element to sweep
4530  * \param newNodesItVec - nodes generated from each node of the element
4531  * \param newElems - generated elements
4532  * \param nbSteps - number of sweeping steps
4533  * \param srcElements - to append elem for each generated element
4534  */
4535 //=======================================================================
4536
4537 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4538                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4539                                     list<const SMDS_MeshElement*>&        newElems,
4540                                     const size_t                          nbSteps,
4541                                     SMESH_SequenceOfElemPtr&              srcElements)
4542 {
4543   SMESHDS_Mesh* aMesh = GetMeshDS();
4544
4545   const int           nbNodes = elem->NbNodes();
4546   const int         nbCorners = elem->NbCornerNodes();
4547   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4548                                                           polyhedron creation !!! */
4549   // Loop on elem nodes:
4550   // find new nodes and detect same nodes indices
4551   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4552   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4553   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4554   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4555
4556   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4557   vector<int> sames(nbNodes);
4558   vector<bool> isSingleNode(nbNodes);
4559
4560   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4561     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4562     const SMDS_MeshNode*                         node = nnIt->first;
4563     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4564     if ( listNewNodes.empty() )
4565       return;
4566
4567     itNN   [ iNode ] = listNewNodes.begin();
4568     prevNod[ iNode ] = node;
4569     nextNod[ iNode ] = listNewNodes.front();
4570
4571     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4572                                                              corner node of linear */
4573     if ( prevNod[ iNode ] != nextNod [ iNode ])
4574       nbDouble += !isSingleNode[iNode];
4575
4576     if( iNode < nbCorners ) { // check corners only
4577       if ( prevNod[ iNode ] == nextNod [ iNode ])
4578         sames[nbSame++] = iNode;
4579       else
4580         iNotSameNode = iNode;
4581     }
4582   }
4583
4584   if ( nbSame == nbNodes || nbSame > 2) {
4585     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4586     return;
4587   }
4588
4589   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4590   {
4591     // fix nodes order to have bottom normal external
4592     if ( baseType == SMDSEntity_Polygon )
4593     {
4594       std::reverse( itNN.begin(), itNN.end() );
4595       std::reverse( prevNod.begin(), prevNod.end() );
4596       std::reverse( midlNod.begin(), midlNod.end() );
4597       std::reverse( nextNod.begin(), nextNod.end() );
4598       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4599     }
4600     else
4601     {
4602       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4603       SMDS_MeshCell::applyInterlace( ind, itNN );
4604       SMDS_MeshCell::applyInterlace( ind, prevNod );
4605       SMDS_MeshCell::applyInterlace( ind, nextNod );
4606       SMDS_MeshCell::applyInterlace( ind, midlNod );
4607       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4608       if ( nbSame > 0 )
4609       {
4610         sames[nbSame] = iNotSameNode;
4611         for ( int j = 0; j <= nbSame; ++j )
4612           for ( size_t i = 0; i < ind.size(); ++i )
4613             if ( ind[i] == sames[j] )
4614             {
4615               sames[j] = i;
4616               break;
4617             }
4618         iNotSameNode = sames[nbSame];
4619       }
4620     }
4621   }
4622   else if ( elem->GetType() == SMDSAbs_Edge )
4623   {
4624     // orient a new face same as adjacent one
4625     int i1, i2;
4626     const SMDS_MeshElement* e;
4627     TIDSortedElemSet dummy;
4628     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4629         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4630         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4631     {
4632       // there is an adjacent face, check order of nodes in it
4633       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4634       if ( sameOrder )
4635       {
4636         std::swap( itNN[0],    itNN[1] );
4637         std::swap( prevNod[0], prevNod[1] );
4638         std::swap( nextNod[0], nextNod[1] );
4639         std::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
7283     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7284     if ( !keepElem )
7285       rmElemIds.push_back( elem->GetID() );
7286
7287     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7288     {
7289       bool elemChanged = false;
7290       if ( i == 0 )
7291       {
7292         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7293           elemChanged = mesh->ChangePolyhedronNodes( elem,
7294                                                      newElemDefs[i].myNodes,
7295                                                      newElemDefs[i].myPolyhedQuantities );
7296         else
7297           elemChanged = mesh->ChangeElementNodes( elem,
7298                                                   & newElemDefs[i].myNodes[0],
7299                                                   newElemDefs[i].myNodes.size() );
7300       }
7301       if ( i > 0 || !elemChanged )
7302       {
7303         if ( i == 0 )
7304         {
7305           newElemDefs[i].SetID( elem->GetID() );
7306           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7307           if ( !keepElem ) rmElemIds.pop_back();
7308         }
7309         else
7310         {
7311           newElemDefs[i].SetID( -1 );
7312         }
7313         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7314         if ( sm && newElem )
7315           sm->AddElement( newElem );
7316         if ( elem != newElem )
7317           ReplaceElemInGroups( elem, newElem, mesh );
7318       }
7319     }
7320   }
7321
7322   // Remove bad elements, then equal nodes (order important)
7323   Remove( rmElemIds, /*isNodes=*/false );
7324   Remove( rmNodeIds, /*isNodes=*/true );
7325
7326   return;
7327 }
7328
7329 //=======================================================================
7330 //function : applyMerge
7331 //purpose  : Compute new connectivity of an element after merging nodes
7332 //  \param [in] elems - the element
7333 //  \param [out] newElemDefs - definition(s) of result element(s)
7334 //  \param [inout] nodeNodeMap - nodes to merge
7335 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7336 //              after merging (but not degenerated), removes nodes causing
7337 //              the invalidity from \a nodeNodeMap.
7338 //  \return bool - true if the element should be removed
7339 //=======================================================================
7340
7341 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7342                                    vector< ElemFeatures >& newElemDefs,
7343                                    TNodeNodeMap&           nodeNodeMap,
7344                                    const bool              avoidMakingHoles )
7345 {
7346   bool toRemove = false; // to remove elem
7347   int nbResElems = 1;    // nb new elements
7348
7349   newElemDefs.resize(nbResElems);
7350   newElemDefs[0].Init( elem );
7351   newElemDefs[0].myNodes.clear();
7352
7353   set<const SMDS_MeshNode*> nodeSet;
7354   vector< const SMDS_MeshNode*>   curNodes;
7355   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7356   vector<int> iRepl;
7357
7358   const        int  nbNodes = elem->NbNodes();
7359   SMDSAbs_EntityType entity = elem->GetEntityType();
7360
7361   curNodes.resize( nbNodes );
7362   uniqueNodes.resize( nbNodes );
7363   iRepl.resize( nbNodes );
7364   int iUnique = 0, iCur = 0, nbRepl = 0;
7365
7366   // Get new seq of nodes
7367
7368   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7369   while ( itN->more() )
7370   {
7371     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7372
7373     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7374     if ( nnIt != nodeNodeMap.end() ) {
7375       n = (*nnIt).second;
7376     }
7377     curNodes[ iCur ] = n;
7378     bool isUnique = nodeSet.insert( n ).second;
7379     if ( isUnique )
7380       uniqueNodes[ iUnique++ ] = n;
7381     else
7382       iRepl[ nbRepl++ ] = iCur;
7383     iCur++;
7384   }
7385
7386   // Analyse element topology after replacement
7387
7388   int nbUniqueNodes = nodeSet.size();
7389   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7390   {
7391     toRemove = true;
7392     nbResElems = 0;
7393
7394     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7395     {
7396       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7397       int nbCorners = nbNodes / 2;
7398       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7399       {
7400         int iNext = ( iCur + 1 ) % nbCorners;
7401         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7402         {
7403           int iMedium = iCur + nbCorners;
7404           vector< const SMDS_MeshNode* >::iterator i =
7405             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7406                        uniqueNodes.end(),
7407                        curNodes[ iMedium ]);
7408           if ( i != uniqueNodes.end() )
7409           {
7410             --nbUniqueNodes;
7411             for ( ; i+1 != uniqueNodes.end(); ++i )
7412               *i = *(i+1);
7413           }
7414         }
7415       }
7416     }
7417
7418     switch ( entity )
7419     {
7420     case SMDSEntity_Polygon:
7421     case SMDSEntity_Quad_Polygon: // Polygon
7422     {
7423       ElemFeatures* elemType = & newElemDefs[0];
7424       const bool isQuad = elemType->myIsQuad;
7425       if ( isQuad )
7426         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7427           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7428
7429       // a polygon can divide into several elements
7430       vector<const SMDS_MeshNode *> polygons_nodes;
7431       vector<int> quantities;
7432       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7433       newElemDefs.resize( nbResElems );
7434       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7435       {
7436         ElemFeatures* elemType = & newElemDefs[iface];
7437         if ( iface ) elemType->Init( elem );
7438
7439         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7440         int nbNewNodes = quantities[iface];
7441         face_nodes.assign( polygons_nodes.begin() + inode,
7442                            polygons_nodes.begin() + inode + nbNewNodes );
7443         inode += nbNewNodes;
7444         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7445         {
7446           bool isValid = ( nbNewNodes % 2 == 0 );
7447           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7448             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7449           elemType->SetQuad( isValid );
7450           if ( isValid ) // put medium nodes after corners
7451             SMDS_MeshCell::applyInterlaceRev
7452               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7453                                                     nbNewNodes ), face_nodes );
7454         }
7455         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7456       }
7457       nbUniqueNodes = newElemDefs[0].myNodes.size();
7458       break;
7459     } // Polygon
7460
7461     case SMDSEntity_Polyhedra: // Polyhedral volume
7462     {
7463       if ( nbUniqueNodes >= 4 )
7464       {
7465         // each face has to be analyzed in order to check volume validity
7466         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7467         {
7468           toRemove = false;
7469           int nbFaces = aPolyedre->NbFaces();
7470
7471           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7472           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7473           vector<const SMDS_MeshNode *>  faceNodes;
7474           poly_nodes.clear();
7475           quantities.clear();
7476
7477           for (int iface = 1; iface <= nbFaces; iface++)
7478           {
7479             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7480             faceNodes.resize( nbFaceNodes );
7481             for (int inode = 1; inode <= nbFaceNodes; inode++)
7482             {
7483               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7484               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7485               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7486                 faceNode = (*nnIt).second;
7487               faceNodes[inode - 1] = faceNode;
7488             }
7489             SimplifyFace(faceNodes, poly_nodes, quantities);
7490           }
7491
7492           if ( quantities.size() > 3 )
7493           {
7494             // TODO: remove coincident faces
7495             nbResElems = 1;
7496             nbUniqueNodes = newElemDefs[0].myNodes.size();
7497           }
7498         }
7499       }
7500     }
7501     break;
7502
7503     // Regular elements
7504     // TODO not all the possible cases are solved. Find something more generic?
7505     case SMDSEntity_Edge: //////// EDGE
7506     case SMDSEntity_Triangle: //// TRIANGLE
7507     case SMDSEntity_Quad_Triangle:
7508     case SMDSEntity_Tetra:
7509     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7510     {
7511       break;
7512     }
7513     case SMDSEntity_Quad_Edge:
7514     {
7515       break;
7516     }
7517     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7518     {
7519       if ( nbUniqueNodes < 3 )
7520         toRemove = true;
7521       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7522         toRemove = true; // opposite nodes stick
7523       else
7524         toRemove = false;
7525       break;
7526     }
7527     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7528     {
7529       //   1    5    2
7530       //    +---+---+
7531       //    |       |
7532       //   4+       +6
7533       //    |       |
7534       //    +---+---+
7535       //   0    7    3
7536       if ( nbUniqueNodes == 6 &&
7537            iRepl[0] < 4       &&
7538            ( nbRepl == 1 || iRepl[1] >= 4 ))
7539       {
7540         toRemove = false;
7541       }
7542       break;
7543     }
7544     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7545     {
7546       //   1    5    2
7547       //    +---+---+
7548       //    |       |
7549       //   4+  8+   +6
7550       //    |       |
7551       //    +---+---+
7552       //   0    7    3
7553       if ( nbUniqueNodes == 7 &&
7554            iRepl[0] < 4       &&
7555            ( nbRepl == 1 || iRepl[1] != 8 ))
7556       {
7557         toRemove = false;
7558       }
7559       break;
7560     }
7561     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7562     {
7563       if ( nbUniqueNodes == 4 ) {
7564         // ---------------------------------> tetrahedron
7565         if ( curNodes[3] == curNodes[4] &&
7566              curNodes[3] == curNodes[5] ) {
7567           // top nodes stick
7568           toRemove = false;
7569         }
7570         else if ( curNodes[0] == curNodes[1] &&
7571                   curNodes[0] == curNodes[2] ) {
7572           // bottom nodes stick: set a top before
7573           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7574           uniqueNodes[ 0 ] = curNodes [ 5 ];
7575           uniqueNodes[ 1 ] = curNodes [ 4 ];
7576           uniqueNodes[ 2 ] = curNodes [ 3 ];
7577           toRemove = false;
7578         }
7579         else if (( curNodes[0] == curNodes[3] ) +
7580                  ( curNodes[1] == curNodes[4] ) +
7581                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7582           // a lateral face turns into a line
7583           toRemove = false;
7584         }
7585       }
7586       else if ( nbUniqueNodes == 5 ) {
7587         // PENTAHEDRON --------------------> pyramid
7588         if ( curNodes[0] == curNodes[3] )
7589         {
7590           uniqueNodes[ 0 ] = curNodes[ 1 ];
7591           uniqueNodes[ 1 ] = curNodes[ 4 ];
7592           uniqueNodes[ 2 ] = curNodes[ 5 ];
7593           uniqueNodes[ 3 ] = curNodes[ 2 ];
7594           uniqueNodes[ 4 ] = curNodes[ 0 ];
7595           toRemove = false;
7596         }
7597         if ( curNodes[1] == curNodes[4] )
7598         {
7599           uniqueNodes[ 0 ] = curNodes[ 0 ];
7600           uniqueNodes[ 1 ] = curNodes[ 2 ];
7601           uniqueNodes[ 2 ] = curNodes[ 5 ];
7602           uniqueNodes[ 3 ] = curNodes[ 3 ];
7603           uniqueNodes[ 4 ] = curNodes[ 1 ];
7604           toRemove = false;
7605         }
7606         if ( curNodes[2] == curNodes[5] )
7607         {
7608           uniqueNodes[ 0 ] = curNodes[ 0 ];
7609           uniqueNodes[ 1 ] = curNodes[ 3 ];
7610           uniqueNodes[ 2 ] = curNodes[ 4 ];
7611           uniqueNodes[ 3 ] = curNodes[ 1 ];
7612           uniqueNodes[ 4 ] = curNodes[ 2 ];
7613           toRemove = false;
7614         }
7615       }
7616       break;
7617     }
7618     case SMDSEntity_Hexa:
7619     {
7620       //////////////////////////////////// HEXAHEDRON
7621       SMDS_VolumeTool hexa (elem);
7622       hexa.SetExternalNormal();
7623       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7624         //////////////////////// HEX ---> tetrahedron
7625         for ( int iFace = 0; iFace < 6; iFace++ ) {
7626           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7627           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7628               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7629               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7630             // one face turns into a point ...
7631             int  pickInd = ind[ 0 ];
7632             int iOppFace = hexa.GetOppFaceIndex( iFace );
7633             ind = hexa.GetFaceNodesIndices( iOppFace );
7634             int nbStick = 0;
7635             uniqueNodes.clear();
7636             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7637               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7638                 nbStick++;
7639               else
7640                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7641             }
7642             if ( nbStick == 1 ) {
7643               // ... and the opposite one - into a triangle.
7644               // set a top node
7645               uniqueNodes.push_back( curNodes[ pickInd ]);
7646               toRemove = false;
7647             }
7648             break;
7649           }
7650         }
7651       }
7652       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7653         //////////////////////// HEX ---> prism
7654         int nbTria = 0, iTria[3];
7655         const int *ind; // indices of face nodes
7656         // look for triangular faces
7657         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7658           ind = hexa.GetFaceNodesIndices( iFace );
7659           TIDSortedNodeSet faceNodes;
7660           for ( iCur = 0; iCur < 4; iCur++ )
7661             faceNodes.insert( curNodes[ind[iCur]] );
7662           if ( faceNodes.size() == 3 )
7663             iTria[ nbTria++ ] = iFace;
7664         }
7665         // check if triangles are opposite
7666         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7667         {
7668           // set nodes of the bottom triangle
7669           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7670           vector<int> indB;
7671           for ( iCur = 0; iCur < 4; iCur++ )
7672             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7673               indB.push_back( ind[iCur] );
7674           if ( !hexa.IsForward() )
7675             std::swap( indB[0], indB[2] );
7676           for ( iCur = 0; iCur < 3; iCur++ )
7677             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7678           // set nodes of the top triangle
7679           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7680           for ( iCur = 0; iCur < 3; ++iCur )
7681             for ( int j = 0; j < 4; ++j )
7682               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7683               {
7684                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7685                 break;
7686               }
7687           toRemove = false;
7688           break;
7689         }
7690       }
7691       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7692         //////////////////// HEXAHEDRON ---> pyramid
7693         for ( int iFace = 0; iFace < 6; iFace++ ) {
7694           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7695           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7696               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7697               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7698             // one face turns into a point ...
7699             int iOppFace = hexa.GetOppFaceIndex( iFace );
7700             ind = hexa.GetFaceNodesIndices( iOppFace );
7701             uniqueNodes.clear();
7702             for ( iCur = 0; iCur < 4; iCur++ ) {
7703               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7704                 break;
7705               else
7706                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7707             }
7708             if ( uniqueNodes.size() == 4 ) {
7709               // ... and the opposite one is a quadrangle
7710               // set a top node
7711               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7712               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7713               toRemove = false;
7714             }
7715             break;
7716           }
7717         }
7718       }
7719
7720       if ( toRemove && nbUniqueNodes > 4 ) {
7721         ////////////////// HEXAHEDRON ---> polyhedron
7722         hexa.SetExternalNormal();
7723         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7724         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7725         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7726         quantities.reserve( 6 );     quantities.clear();
7727         for ( int iFace = 0; iFace < 6; iFace++ )
7728         {
7729           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7730           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7731                curNodes[ind[1]] == curNodes[ind[3]] )
7732           {
7733             quantities.clear();
7734             break; // opposite nodes stick
7735           }
7736           nodeSet.clear();
7737           for ( iCur = 0; iCur < 4; iCur++ )
7738           {
7739             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7740               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7741           }
7742           if ( nodeSet.size() < 3 )
7743             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7744           else
7745             quantities.push_back( nodeSet.size() );
7746         }
7747         if ( quantities.size() >= 4 )
7748         {
7749           nbResElems = 1;
7750           nbUniqueNodes = poly_nodes.size();
7751           newElemDefs[0].SetPoly(true);
7752         }
7753       }
7754       break;
7755     } // case HEXAHEDRON
7756
7757     default:
7758       toRemove = true;
7759
7760     } // switch ( entity )
7761
7762     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7763     {
7764       // erase from nodeNodeMap nodes whose merge spoils elem
7765       vector< const SMDS_MeshNode* > noMergeNodes;
7766       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7767       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7768         nodeNodeMap.erase( noMergeNodes[i] );
7769     }
7770     
7771   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7772
7773   uniqueNodes.resize( nbUniqueNodes );
7774
7775   if ( !toRemove && nbResElems == 0 )
7776     nbResElems = 1;
7777
7778   newElemDefs.resize( nbResElems );
7779
7780   return !toRemove;
7781 }
7782
7783
7784 // ========================================================
7785 // class   : ComparableElement
7786 // purpose : allow comparing elements basing on their nodes
7787 // ========================================================
7788
7789 class ComparableElement : public boost::container::flat_set< smIdType >
7790 {
7791   typedef boost::container::flat_set< smIdType >  int_set;
7792
7793   const SMDS_MeshElement* myElem;
7794   smIdType                mySumID;
7795   mutable int             myGroupID;
7796
7797 public:
7798
7799   ComparableElement( const SMDS_MeshElement* theElem ):
7800     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7801   {
7802     this->reserve( theElem->NbNodes() );
7803     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7804     {
7805       smIdType id = nodeIt->next()->GetID();
7806       mySumID += id;
7807       this->insert( id );
7808     }
7809   }
7810
7811   const SMDS_MeshElement* GetElem() const { return myElem; }
7812
7813   int& GroupID() const { return myGroupID; }
7814   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7815
7816   ComparableElement( const ComparableElement& theSource ) // move copy
7817     : int_set()
7818   {
7819     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7820     (int_set&) (*this ) = std::move( src );
7821     myElem    = src.myElem;
7822     mySumID   = src.mySumID;
7823     myGroupID = src.myGroupID;
7824   }
7825
7826   static int HashCode(const ComparableElement& se, int limit )
7827   {
7828     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7829   }
7830   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7831   {
7832     return ( se1 == se2 );
7833   }
7834
7835 };
7836
7837 //=======================================================================
7838 //function : FindEqualElements
7839 //purpose  : Return list of group of elements built on the same nodes.
7840 //           Search among theElements or in the whole mesh if theElements is empty
7841 //=======================================================================
7842
7843 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7844                                           TListOfListOfElementsID & theGroupsOfElementsID )
7845 {
7846   ClearLastCreated();
7847
7848   SMDS_ElemIteratorPtr elemIt;
7849   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7850   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7851
7852   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7853   typedef std::list<smIdType>                                     TGroupOfElems;
7854   TMapOfElements               mapOfElements;
7855   std::vector< TGroupOfElems > arrayOfGroups;
7856   TGroupOfElems                groupOfElems;
7857
7858   while ( elemIt->more() )
7859   {
7860     const SMDS_MeshElement* curElem = elemIt->next();
7861     if ( curElem->IsNull() )
7862       continue;
7863     ComparableElement      compElem = curElem;
7864     // check uniqueness
7865     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7866     if ( elemInSet.GetElem() != curElem ) // coincident elem
7867     {
7868       int& iG = elemInSet.GroupID();
7869       if ( iG < 0 )
7870       {
7871         iG = arrayOfGroups.size();
7872         arrayOfGroups.push_back( groupOfElems );
7873         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7874       }
7875       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7876     }
7877   }
7878
7879   groupOfElems.clear();
7880   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7881   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7882   {
7883     if ( groupIt->size() > 1 ) {
7884       //groupOfElems.sort(); -- theElements are sorted already
7885       theGroupsOfElementsID.emplace_back( *groupIt );
7886     }
7887   }
7888 }
7889
7890 //=======================================================================
7891 //function : MergeElements
7892 //purpose  : In each given group, substitute all elements by the first one.
7893 //=======================================================================
7894
7895 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7896 {
7897   ClearLastCreated();
7898
7899   typedef list<smIdType> TListOfIDs;
7900   TListOfIDs rmElemIds; // IDs of elems to remove
7901
7902   SMESHDS_Mesh* aMesh = GetMeshDS();
7903
7904   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7905   while ( groupsIt != theGroupsOfElementsID.end() ) {
7906     TListOfIDs& aGroupOfElemID = *groupsIt;
7907     aGroupOfElemID.sort();
7908     int elemIDToKeep = aGroupOfElemID.front();
7909     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7910     aGroupOfElemID.pop_front();
7911     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7912     while ( idIt != aGroupOfElemID.end() ) {
7913       int elemIDToRemove = *idIt;
7914       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7915       // add the kept element in groups of removed one (PAL15188)
7916       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7917       rmElemIds.push_back( elemIDToRemove );
7918       ++idIt;
7919     }
7920     ++groupsIt;
7921   }
7922
7923   Remove( rmElemIds, false );
7924 }
7925
7926 //=======================================================================
7927 //function : MergeEqualElements
7928 //purpose  : Remove all but one of elements built on the same nodes.
7929 //=======================================================================
7930
7931 void SMESH_MeshEditor::MergeEqualElements()
7932 {
7933   TIDSortedElemSet aMeshElements; /* empty input ==
7934                                      to merge equal elements in the whole mesh */
7935   TListOfListOfElementsID aGroupsOfElementsID;
7936   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7937   MergeElements( aGroupsOfElementsID );
7938 }
7939
7940 //=======================================================================
7941 //function : findAdjacentFace
7942 //purpose  :
7943 //=======================================================================
7944
7945 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7946                                                 const SMDS_MeshNode* n2,
7947                                                 const SMDS_MeshElement* elem)
7948 {
7949   TIDSortedElemSet elemSet, avoidSet;
7950   if ( elem )
7951     avoidSet.insert ( elem );
7952   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7953 }
7954
7955 //=======================================================================
7956 //function : findSegment
7957 //purpose  : Return a mesh segment by two nodes one of which can be medium
7958 //=======================================================================
7959
7960 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7961                                            const SMDS_MeshNode* n2)
7962 {
7963   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7964   while ( it->more() )
7965   {
7966     const SMDS_MeshElement* seg = it->next();
7967     if ( seg->GetNodeIndex( n2 ) >= 0 )
7968       return seg;
7969   }
7970   return 0;
7971 }
7972
7973 //=======================================================================
7974 //function : FindFreeBorder
7975 //purpose  :
7976 //=======================================================================
7977
7978 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7979
7980 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7981                                        const SMDS_MeshNode*             theSecondNode,
7982                                        const SMDS_MeshNode*             theLastNode,
7983                                        list< const SMDS_MeshNode* > &   theNodes,
7984                                        list< const SMDS_MeshElement* >& theFaces)
7985 {
7986   if ( !theFirstNode || !theSecondNode )
7987     return false;
7988   // find border face between theFirstNode and theSecondNode
7989   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7990   if ( !curElem )
7991     return false;
7992
7993   theFaces.push_back( curElem );
7994   theNodes.push_back( theFirstNode );
7995   theNodes.push_back( theSecondNode );
7996
7997   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7998   //TIDSortedElemSet foundElems;
7999   bool needTheLast = ( theLastNode != 0 );
8000
8001   vector<const SMDS_MeshNode*> nodes;
8002   
8003   while ( nStart != theLastNode ) {
8004     if ( nStart == theFirstNode )
8005       return !needTheLast;
8006
8007     // find all free border faces sharing nStart
8008
8009     list< const SMDS_MeshElement* > curElemList;
8010     list< const SMDS_MeshNode* >    nStartList;
8011     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8012     while ( invElemIt->more() ) {
8013       const SMDS_MeshElement* e = invElemIt->next();
8014       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8015       {
8016         // get nodes
8017         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8018                       SMDS_MeshElement::iterator() );
8019         nodes.push_back( nodes[ 0 ]);
8020
8021         // check 2 links
8022         int iNode = 0, nbNodes = nodes.size() - 1;
8023         for ( iNode = 0; iNode < nbNodes; iNode++ )
8024           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8025                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8026               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8027           {
8028             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8029             curElemList.push_back( e );
8030           }
8031       }
8032     }
8033     // analyse the found
8034
8035     int nbNewBorders = curElemList.size();
8036     if ( nbNewBorders == 0 ) {
8037       // no free border furthermore
8038       return !needTheLast;
8039     }
8040     else if ( nbNewBorders == 1 ) {
8041       // one more element found
8042       nIgnore = nStart;
8043       nStart = nStartList.front();
8044       curElem = curElemList.front();
8045       theFaces.push_back( curElem );
8046       theNodes.push_back( nStart );
8047     }
8048     else {
8049       // several continuations found
8050       list< const SMDS_MeshElement* >::iterator curElemIt;
8051       list< const SMDS_MeshNode* >::iterator nStartIt;
8052       // check if one of them reached the last node
8053       if ( needTheLast ) {
8054         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8055              curElemIt!= curElemList.end();
8056              curElemIt++, nStartIt++ )
8057           if ( *nStartIt == theLastNode ) {
8058             theFaces.push_back( *curElemIt );
8059             theNodes.push_back( *nStartIt );
8060             return true;
8061           }
8062       }
8063       // find the best free border by the continuations
8064       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8065       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8066       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8067            curElemIt!= curElemList.end();
8068            curElemIt++, nStartIt++ )
8069       {
8070         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8071         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8072         // find one more free border
8073         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8074           cNL->clear();
8075           cFL->clear();
8076         }
8077         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8078           // choice: clear a worse one
8079           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8080           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8081           contNodes[ iWorse ].clear();
8082           contFaces[ iWorse ].clear();
8083         }
8084       }
8085       if ( contNodes[0].empty() && contNodes[1].empty() )
8086         return false;
8087
8088       // push_back the best free border
8089       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8090       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8091       //theNodes.pop_back(); // remove nIgnore
8092       theNodes.pop_back(); // remove nStart
8093       //theFaces.pop_back(); // remove curElem
8094       theNodes.splice( theNodes.end(), *cNL );
8095       theFaces.splice( theFaces.end(), *cFL );
8096       return true;
8097
8098     } // several continuations found
8099   } // while ( nStart != theLastNode )
8100
8101   return true;
8102 }
8103
8104 //=======================================================================
8105 //function : CheckFreeBorderNodes
8106 //purpose  : Return true if the tree nodes are on a free border
8107 //=======================================================================
8108
8109 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8110                                             const SMDS_MeshNode* theNode2,
8111                                             const SMDS_MeshNode* theNode3)
8112 {
8113   list< const SMDS_MeshNode* > nodes;
8114   list< const SMDS_MeshElement* > faces;
8115   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8116 }
8117
8118 //=======================================================================
8119 //function : SewFreeBorder
8120 //purpose  :
8121 //warning  : for border-to-side sewing theSideSecondNode is considered as
8122 //           the last side node and theSideThirdNode is not used
8123 //=======================================================================
8124
8125 SMESH_MeshEditor::Sew_Error
8126 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8127                                  const SMDS_MeshNode* theBordSecondNode,
8128                                  const SMDS_MeshNode* theBordLastNode,
8129                                  const SMDS_MeshNode* theSideFirstNode,
8130                                  const SMDS_MeshNode* theSideSecondNode,
8131                                  const SMDS_MeshNode* theSideThirdNode,
8132                                  const bool           theSideIsFreeBorder,
8133                                  const bool           toCreatePolygons,
8134                                  const bool           toCreatePolyedrs)
8135 {
8136   ClearLastCreated();
8137
8138   Sew_Error aResult = SEW_OK;
8139
8140   // ====================================
8141   //    find side nodes and elements
8142   // ====================================
8143
8144   list< const SMDS_MeshNode* >    nSide[ 2 ];
8145   list< const SMDS_MeshElement* > eSide[ 2 ];
8146   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8147   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8148
8149   // Free border 1
8150   // --------------
8151   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8152                       nSide[0], eSide[0])) {
8153     MESSAGE(" Free Border 1 not found " );
8154     aResult = SEW_BORDER1_NOT_FOUND;
8155   }
8156   if (theSideIsFreeBorder) {
8157     // Free border 2
8158     // --------------
8159     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8160                         nSide[1], eSide[1])) {
8161       MESSAGE(" Free Border 2 not found " );
8162       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8163     }
8164   }
8165   if ( aResult != SEW_OK )
8166     return aResult;
8167
8168   if (!theSideIsFreeBorder) {
8169     // Side 2
8170     // --------------
8171
8172     // -------------------------------------------------------------------------
8173     // Algo:
8174     // 1. If nodes to merge are not coincident, move nodes of the free border
8175     //    from the coord sys defined by the direction from the first to last
8176     //    nodes of the border to the correspondent sys of the side 2
8177     // 2. On the side 2, find the links most co-directed with the correspondent
8178     //    links of the free border
8179     // -------------------------------------------------------------------------
8180
8181     // 1. Since sewing may break if there are volumes to split on the side 2,
8182     //    we won't move nodes but just compute new coordinates for them
8183     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8184     TNodeXYZMap nBordXYZ;
8185     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8186     list< const SMDS_MeshNode* >::iterator nBordIt;
8187
8188     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8189     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8190     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8191     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8192     double tol2 = 1.e-8;
8193     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8194     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8195       // Need node movement.
8196
8197       // find X and Z axes to create trsf
8198       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8199       gp_Vec X = Zs ^ Zb;
8200       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8201         // Zb || Zs
8202         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8203
8204       // coord systems
8205       gp_Ax3 toBordAx( Pb1, Zb, X );
8206       gp_Ax3 fromSideAx( Ps1, Zs, X );
8207       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8208       // set trsf
8209       gp_Trsf toBordSys, fromSide2Sys;
8210       toBordSys.SetTransformation( toBordAx );
8211       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8212       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8213
8214       // move
8215       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8216         const SMDS_MeshNode* n = *nBordIt;
8217         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8218         toBordSys.Transforms( xyz );
8219         fromSide2Sys.Transforms( xyz );
8220         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8221       }
8222     }
8223     else {
8224       // just insert nodes XYZ in the nBordXYZ map
8225       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8226         const SMDS_MeshNode* n = *nBordIt;
8227         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8228       }
8229     }
8230
8231     // 2. On the side 2, find the links most co-directed with the correspondent
8232     //    links of the free border
8233
8234     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8235     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8236     sideNodes.push_back( theSideFirstNode );
8237
8238     bool hasVolumes = false;
8239     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8240     set<long> foundSideLinkIDs, checkedLinkIDs;
8241     SMDS_VolumeTool volume;
8242     //const SMDS_MeshNode* faceNodes[ 4 ];
8243
8244     const SMDS_MeshNode*    sideNode;
8245     const SMDS_MeshElement* sideElem  = 0;
8246     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8247     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8248     nBordIt = bordNodes.begin();
8249     nBordIt++;
8250     // border node position and border link direction to compare with
8251     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8252     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8253     // choose next side node by link direction or by closeness to
8254     // the current border node:
8255     bool searchByDir = ( *nBordIt != theBordLastNode );
8256     do {
8257       // find the next node on the Side 2
8258       sideNode = 0;
8259       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8260       long linkID;
8261       checkedLinkIDs.clear();
8262       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8263
8264       // loop on inverse elements of current node (prevSideNode) on the Side 2
8265       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8266       while ( invElemIt->more() )
8267       {
8268         const SMDS_MeshElement* elem = invElemIt->next();
8269         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8270         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8271         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8272         bool isVolume = volume.Set( elem );
8273         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8274         if ( isVolume ) // --volume
8275           hasVolumes = true;
8276         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8277           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8278           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8279           while ( nIt->more() ) {
8280             nodes[ iNode ] = cast2Node( nIt->next() );
8281             if ( nodes[ iNode++ ] == prevSideNode )
8282               iPrevNode = iNode - 1;
8283           }
8284           // there are 2 links to check
8285           nbNodes = 2;
8286         }
8287         else // --edge
8288           continue;
8289         // loop on links, to be precise, on the second node of links
8290         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8291           const SMDS_MeshNode* n = nodes[ iNode ];
8292           if ( isVolume ) {
8293             if ( !volume.IsLinked( n, prevSideNode ))
8294               continue;
8295           }
8296           else {
8297             if ( iNode ) // a node before prevSideNode
8298               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8299             else         // a node after prevSideNode
8300               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8301           }
8302           // check if this link was already used
8303           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8304           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8305           if (!isJustChecked &&
8306               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8307           {
8308             // test a link geometrically
8309             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8310             bool linkIsBetter = false;
8311             double dot = 0.0, dist = 0.0;
8312             if ( searchByDir ) { // choose most co-directed link
8313               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8314               linkIsBetter = ( dot > maxDot );
8315             }
8316             else { // choose link with the node closest to bordPos
8317               dist = ( nextXYZ - bordPos ).SquareModulus();
8318               linkIsBetter = ( dist < minDist );
8319             }
8320             if ( linkIsBetter ) {
8321               maxDot = dot;
8322               minDist = dist;
8323               linkID = iLink;
8324               sideNode = n;
8325               sideElem = elem;
8326             }
8327           }
8328         }
8329       } // loop on inverse elements of prevSideNode
8330
8331       if ( !sideNode ) {
8332         MESSAGE(" Can't find path by links of the Side 2 ");
8333         return SEW_BAD_SIDE_NODES;
8334       }
8335       sideNodes.push_back( sideNode );
8336       sideElems.push_back( sideElem );
8337       foundSideLinkIDs.insert ( linkID );
8338       prevSideNode = sideNode;
8339
8340       if ( *nBordIt == theBordLastNode )
8341         searchByDir = false;
8342       else {
8343         // find the next border link to compare with
8344         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8345         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8346         // move to next border node if sideNode is before forward border node (bordPos)
8347         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8348           prevBordNode = *nBordIt;
8349           nBordIt++;
8350           bordPos = nBordXYZ[ *nBordIt ];
8351           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8352           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8353         }
8354       }
8355     }
8356     while ( sideNode != theSideSecondNode );
8357
8358     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8359       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8360       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8361     }
8362   } // end nodes search on the side 2
8363
8364   // ============================
8365   // sew the border to the side 2
8366   // ============================
8367
8368   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8369   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8370
8371   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8372   if ( toMergeConformal && toCreatePolygons )
8373   {
8374     // do not merge quadrangles if polygons are OK (IPAL0052824)
8375     eIt[0] = eSide[0].begin();
8376     eIt[1] = eSide[1].begin();
8377     bool allQuads[2] = { true, true };
8378     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8379       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8380         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8381     }
8382     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8383   }
8384
8385   TListOfListOfNodes nodeGroupsToMerge;
8386   if (( toMergeConformal ) ||
8387       ( theSideIsFreeBorder && !theSideThirdNode )) {
8388
8389     // all nodes are to be merged
8390
8391     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8392          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8393          nIt[0]++, nIt[1]++ )
8394     {
8395       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8397       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8398     }
8399   }
8400   else {
8401
8402     // insert new nodes into the border and the side to get equal nb of segments
8403
8404     // get normalized parameters of nodes on the borders
8405     vector< double > param[ 2 ];
8406     param[0].resize( maxNbNodes );
8407     param[1].resize( maxNbNodes );
8408     int iNode, iBord;
8409     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8410       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8411       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8412       const SMDS_MeshNode* nPrev = *nIt;
8413       double bordLength = 0;
8414       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8415         const SMDS_MeshNode* nCur = *nIt;
8416         gp_XYZ segment (nCur->X() - nPrev->X(),
8417                         nCur->Y() - nPrev->Y(),
8418                         nCur->Z() - nPrev->Z());
8419         double segmentLen = segment.Modulus();
8420         bordLength += segmentLen;
8421         param[ iBord ][ iNode ] = bordLength;
8422         nPrev = nCur;
8423       }
8424       // normalize within [0,1]
8425       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8426         param[ iBord ][ iNode ] /= bordLength;
8427       }
8428     }
8429
8430     // loop on border segments
8431     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8432     int i[ 2 ] = { 0, 0 };
8433     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8434     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8435
8436     // element can be split while iterating on border if it has two edges in the border
8437     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8438     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8439
8440     TElemOfNodeListMap insertMap;
8441     TElemOfNodeListMap::iterator insertMapIt;
8442     // insertMap is
8443     // key:   elem to insert nodes into
8444     // value: 2 nodes to insert between + nodes to be inserted
8445     do {
8446       bool next[ 2 ] = { false, false };
8447
8448       // find min adjacent segment length after sewing
8449       double nextParam = 10., prevParam = 0;
8450       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8451         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8452           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8453         if ( i[ iBord ] > 0 )
8454           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8455       }
8456       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8457       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8458       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8459
8460       // choose to insert or to merge nodes
8461       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8462       if ( Abs( du ) <= minSegLen * 0.2 ) {
8463         // merge
8464         // ------
8465         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8466         const SMDS_MeshNode* n0 = *nIt[0];
8467         const SMDS_MeshNode* n1 = *nIt[1];
8468         nodeGroupsToMerge.back().push_back( n1 );
8469         nodeGroupsToMerge.back().push_back( n0 );
8470         // position of node of the border changes due to merge
8471         param[ 0 ][ i[0] ] += du;
8472         // move n1 for the sake of elem shape evaluation during insertion.
8473         // n1 will be removed by MergeNodes() anyway
8474         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8475         next[0] = next[1] = true;
8476       }
8477       else {
8478         // insert
8479         // ------
8480         int intoBord = ( du < 0 ) ? 0 : 1;
8481         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8482         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8483         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8484         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8485         if ( intoBord == 1 ) {
8486           // move node of the border to be on a link of elem of the side
8487           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8488           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8489           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8490           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8491         }
8492         elemReplaceMapIt = elemReplaceMap.find( elem );
8493         if ( elemReplaceMapIt != elemReplaceMap.end() )
8494           elem = elemReplaceMapIt->second;
8495
8496         insertMapIt = insertMap.find( elem );
8497         bool  notFound = ( insertMapIt == insertMap.end() );
8498         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8499         if ( otherLink ) {
8500           // insert into another link of the same element:
8501           // 1. perform insertion into the other link of the elem
8502           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8503           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8504           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8505           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8506           // 2. perform insertion into the link of adjacent faces
8507           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8508             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8509           }
8510           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8511             InsertNodesIntoLink( seg, n12, n22, nodeList );
8512           }
8513           if (toCreatePolyedrs) {
8514             // perform insertion into the links of adjacent volumes
8515             UpdateVolumes(n12, n22, nodeList);
8516           }
8517           // 3. find an element appeared on n1 and n2 after the insertion
8518           insertMap.erase( insertMapIt );
8519           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8520           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8521           elem = elem2;
8522         }
8523         if ( notFound || otherLink ) {
8524           // add element and nodes of the side into the insertMap
8525           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8526           (*insertMapIt).second.push_back( n1 );
8527           (*insertMapIt).second.push_back( n2 );
8528         }
8529         // add node to be inserted into elem
8530         (*insertMapIt).second.push_back( nIns );
8531         next[ 1 - intoBord ] = true;
8532       }
8533
8534       // go to the next segment
8535       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8536         if ( next[ iBord ] ) {
8537           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8538             eIt[ iBord ]++;
8539           nPrev[ iBord ] = *nIt[ iBord ];
8540           nIt[ iBord ]++; i[ iBord ]++;
8541         }
8542       }
8543     }
8544     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8545
8546     // perform insertion of nodes into elements
8547
8548     for (insertMapIt = insertMap.begin();
8549          insertMapIt != insertMap.end();
8550          insertMapIt++ )
8551     {
8552       const SMDS_MeshElement* elem = (*insertMapIt).first;
8553       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8554       if ( nodeList.size() < 3 ) continue;
8555       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8556       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8557
8558       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8559
8560       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8561         InsertNodesIntoLink( seg, n1, n2, nodeList );
8562       }
8563
8564       if ( !theSideIsFreeBorder ) {
8565         // look for and insert nodes into the faces adjacent to elem
8566         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8567           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8568         }
8569       }
8570       if (toCreatePolyedrs) {
8571         // perform insertion into the links of adjacent volumes
8572         UpdateVolumes(n1, n2, nodeList);
8573       }
8574     }
8575   } // end: insert new nodes
8576
8577   MergeNodes ( nodeGroupsToMerge );
8578
8579
8580   // Remove coincident segments
8581
8582   // get new segments
8583   TIDSortedElemSet segments;
8584   SMESH_SequenceOfElemPtr newFaces;
8585   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8586   {
8587     if ( !myLastCreatedElems[i] ) continue;
8588     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8589       segments.insert( segments.end(), myLastCreatedElems[i] );
8590     else
8591       newFaces.push_back( myLastCreatedElems[i] );
8592   }
8593   // get segments adjacent to merged nodes
8594   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8595   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8596   {
8597     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8598     if ( nodes.front()->IsNull() ) continue;
8599     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8600     while ( segIt->more() )
8601       segments.insert( segIt->next() );
8602   }
8603
8604   // find coincident
8605   TListOfListOfElementsID equalGroups;
8606   if ( !segments.empty() )
8607     FindEqualElements( segments, equalGroups );
8608   if ( !equalGroups.empty() )
8609   {
8610     // remove from segments those that will be removed
8611     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8612     for ( ; itGroups != equalGroups.end(); ++itGroups )
8613     {
8614       list< smIdType >& group = *itGroups;
8615       list< smIdType >::iterator id = group.begin();
8616       for ( ++id; id != group.end(); ++id )
8617         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8618           segments.erase( seg );
8619     }
8620     // remove equal segments
8621     MergeElements( equalGroups );
8622
8623     // restore myLastCreatedElems
8624     myLastCreatedElems = newFaces;
8625     TIDSortedElemSet::iterator seg = segments.begin();
8626     for ( ; seg != segments.end(); ++seg )
8627       myLastCreatedElems.push_back( *seg );
8628   }
8629
8630   return aResult;
8631 }
8632
8633 //=======================================================================
8634 //function : InsertNodesIntoLink
8635 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8636 //           and theBetweenNode2 and split theElement
8637 //=======================================================================
8638
8639 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8640                                            const SMDS_MeshNode*        theBetweenNode1,
8641                                            const SMDS_MeshNode*        theBetweenNode2,
8642                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8643                                            const bool                  toCreatePoly)
8644 {
8645   if ( !theElement ) return;
8646
8647   SMESHDS_Mesh *aMesh = GetMeshDS();
8648   vector<const SMDS_MeshElement*> newElems;
8649
8650   if ( theElement->GetType() == SMDSAbs_Edge )
8651   {
8652     theNodesToInsert.push_front( theBetweenNode1 );
8653     theNodesToInsert.push_back ( theBetweenNode2 );
8654     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8655     const SMDS_MeshNode* n1 = *n;
8656     for ( ++n; n != theNodesToInsert.end(); ++n )
8657     {
8658       const SMDS_MeshNode* n2 = *n;
8659       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8660         AddToSameGroups( seg, theElement, aMesh );
8661       else
8662         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8663       n1 = n2;
8664     }
8665     theNodesToInsert.pop_front();
8666     theNodesToInsert.pop_back();
8667
8668     if ( theElement->IsQuadratic() ) // add a not split part
8669     {
8670       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8671                                           theElement->end_nodes() );
8672       int iOther = 0, nbN = nodes.size();
8673       for ( ; iOther < nbN; ++iOther )
8674         if ( nodes[iOther] != theBetweenNode1 &&
8675              nodes[iOther] != theBetweenNode2 )
8676           break;
8677       if      ( iOther == 0 )
8678       {
8679         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8680           AddToSameGroups( seg, theElement, aMesh );
8681         else
8682           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8683       }
8684       else if ( iOther == 2 )
8685       {
8686         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8687           AddToSameGroups( seg, theElement, aMesh );
8688         else
8689           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8690       }
8691     }
8692     // treat new elements
8693     for ( size_t i = 0; i < newElems.size(); ++i )
8694       if ( newElems[i] )
8695       {
8696         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8697         myLastCreatedElems.push_back( newElems[i] );
8698       }
8699     ReplaceElemInGroups( theElement, newElems, aMesh );
8700     aMesh->RemoveElement( theElement );
8701     return;
8702
8703   } // if ( theElement->GetType() == SMDSAbs_Edge )
8704
8705   const SMDS_MeshElement* theFace = theElement;
8706   if ( theFace->GetType() != SMDSAbs_Face ) return;
8707
8708   // find indices of 2 link nodes and of the rest nodes
8709   int iNode = 0, il1, il2, i3, i4;
8710   il1 = il2 = i3 = i4 = -1;
8711   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8712
8713   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8714   while ( nodeIt->more() ) {
8715     const SMDS_MeshNode* n = nodeIt->next();
8716     if ( n == theBetweenNode1 )
8717       il1 = iNode;
8718     else if ( n == theBetweenNode2 )
8719       il2 = iNode;
8720     else if ( i3 < 0 )
8721       i3 = iNode;
8722     else
8723       i4 = iNode;
8724     nodes[ iNode++ ] = n;
8725   }
8726   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8727     return ;
8728
8729   // arrange link nodes to go one after another regarding the face orientation
8730   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8731   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8732   if ( reverse ) {
8733     iNode = il1;
8734     il1 = il2;
8735     il2 = iNode;
8736     aNodesToInsert.reverse();
8737   }
8738   // check that not link nodes of a quadrangles are in good order
8739   int nbFaceNodes = theFace->NbNodes();
8740   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8741     iNode = i3;
8742     i3 = i4;
8743     i4 = iNode;
8744   }
8745
8746   if (toCreatePoly || theFace->IsPoly()) {
8747
8748     iNode = 0;
8749     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8750
8751     // add nodes of face up to first node of link
8752     bool isFLN = false;
8753     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8754     while ( nodeIt->more() && !isFLN ) {
8755       const SMDS_MeshNode* n = nodeIt->next();
8756       poly_nodes[iNode++] = n;
8757       isFLN = ( n == nodes[il1] );
8758     }
8759     // add nodes to insert
8760     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8761     for (; nIt != aNodesToInsert.end(); nIt++) {
8762       poly_nodes[iNode++] = *nIt;
8763     }
8764     // add nodes of face starting from last node of link
8765     while ( nodeIt->more() ) {
8766       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8767       poly_nodes[iNode++] = n;
8768     }
8769
8770     // make a new face
8771     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8772   }
8773
8774   else if ( !theFace->IsQuadratic() )
8775   {
8776     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8777     int nbLinkNodes = 2 + aNodesToInsert.size();
8778     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8779     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8780     linkNodes[ 0 ] = nodes[ il1 ];
8781     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8782     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8783     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8784       linkNodes[ iNode++ ] = *nIt;
8785     }
8786     // decide how to split a quadrangle: compare possible variants
8787     // and choose which of splits to be a quadrangle
8788     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8789     if ( nbFaceNodes == 3 ) {
8790       iBestQuad = nbSplits;
8791       i4 = i3;
8792     }
8793     else if ( nbFaceNodes == 4 ) {
8794       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8795       double aBestRate = DBL_MAX;
8796       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8797         i1 = 0; i2 = 1;
8798         double aBadRate = 0;
8799         // evaluate elements quality
8800         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8801           if ( iSplit == iQuad ) {
8802             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8803                                    linkNodes[ i2++ ],
8804                                    nodes[ i3 ],
8805                                    nodes[ i4 ]);
8806             aBadRate += getBadRate( &quad, aCrit );
8807           }
8808           else {
8809             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8810                                    linkNodes[ i2++ ],
8811                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8812             aBadRate += getBadRate( &tria, aCrit );
8813           }
8814         }
8815         // choice
8816         if ( aBadRate < aBestRate ) {
8817           iBestQuad = iQuad;
8818           aBestRate = aBadRate;
8819         }
8820       }
8821     }
8822
8823     // create new elements
8824     i1 = 0; i2 = 1;
8825     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8826     {
8827       if ( iSplit == iBestQuad )
8828         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8829                                             linkNodes[ i2++ ],
8830                                             nodes[ i3 ],
8831                                             nodes[ i4 ]));
8832       else
8833         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8834                                             linkNodes[ i2++ ],
8835                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8836     }
8837
8838     const SMDS_MeshNode* newNodes[ 4 ];
8839     newNodes[ 0 ] = linkNodes[ i1 ];
8840     newNodes[ 1 ] = linkNodes[ i2 ];
8841     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8842     newNodes[ 3 ] = nodes[ i4 ];
8843     if (iSplit == iBestQuad)
8844       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8845     else
8846       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8847
8848   } // end if(!theFace->IsQuadratic())
8849
8850   else { // theFace is quadratic
8851     // we have to split theFace on simple triangles and one simple quadrangle
8852     int tmp = il1/2;
8853     int nbshift = tmp*2;
8854     // shift nodes in nodes[] by nbshift
8855     int i,j;
8856     for(i=0; i<nbshift; i++) {
8857       const SMDS_MeshNode* n = nodes[0];
8858       for(j=0; j<nbFaceNodes-1; j++) {
8859         nodes[j] = nodes[j+1];
8860       }
8861       nodes[nbFaceNodes-1] = n;
8862     }
8863     il1 = il1 - nbshift;
8864     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8865     //   n0      n1     n2    n0      n1     n2
8866     //     +-----+-----+        +-----+-----+
8867     //      \         /         |           |
8868     //       \       /          |           |
8869     //      n5+     +n3       n7+           +n3
8870     //         \   /            |           |
8871     //          \ /             |           |
8872     //           +              +-----+-----+
8873     //           n4           n6      n5     n4
8874
8875     // create new elements
8876     int n1,n2,n3;
8877     if ( nbFaceNodes == 6 ) { // quadratic triangle
8878       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8879       if ( theFace->IsMediumNode(nodes[il1]) ) {
8880         // create quadrangle
8881         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8882         n1 = 1;
8883         n2 = 2;
8884         n3 = 3;
8885       }
8886       else {
8887         // create quadrangle
8888         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8889         n1 = 0;
8890         n2 = 1;
8891         n3 = 5;
8892       }
8893     }
8894     else { // nbFaceNodes==8 - quadratic quadrangle
8895       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8896       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8897       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8898       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8899         // create quadrangle
8900         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8901         n1 = 1;
8902         n2 = 2;
8903         n3 = 3;
8904       }
8905       else {
8906         // create quadrangle
8907         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8908         n1 = 0;
8909         n2 = 1;
8910         n3 = 7;
8911       }
8912     }
8913     // create needed triangles using n1,n2,n3 and inserted nodes
8914     int nbn = 2 + aNodesToInsert.size();
8915     vector<const SMDS_MeshNode*> aNodes(nbn);
8916     aNodes[0    ] = nodes[n1];
8917     aNodes[nbn-1] = nodes[n2];
8918     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8919     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8920       aNodes[iNode++] = *nIt;
8921     }
8922     for ( i = 1; i < nbn; i++ )
8923       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8924   }
8925
8926   // remove the old face
8927   for ( size_t i = 0; i < newElems.size(); ++i )
8928     if ( newElems[i] )
8929     {
8930       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8931       myLastCreatedElems.push_back( newElems[i] );
8932     }
8933   ReplaceElemInGroups( theFace, newElems, aMesh );
8934   aMesh->RemoveElement(theFace);
8935
8936 } // InsertNodesIntoLink()
8937
8938 //=======================================================================
8939 //function : UpdateVolumes
8940 //purpose  :
8941 //=======================================================================
8942
8943 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8944                                       const SMDS_MeshNode*        theBetweenNode2,
8945                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8946 {
8947   ClearLastCreated();
8948
8949   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8950   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8951     const SMDS_MeshElement* elem = invElemIt->next();
8952
8953     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8954     SMDS_VolumeTool aVolume (elem);
8955     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8956       continue;
8957
8958     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8959     int iface, nbFaces = aVolume.NbFaces();
8960     vector<const SMDS_MeshNode *> poly_nodes;
8961     vector<int> quantities (nbFaces);
8962
8963     for (iface = 0; iface < nbFaces; iface++) {
8964       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8965       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8966       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8967
8968       for (int inode = 0; inode < nbFaceNodes; inode++) {
8969         poly_nodes.push_back(faceNodes[inode]);
8970
8971         if (nbInserted == 0) {
8972           if (faceNodes[inode] == theBetweenNode1) {
8973             if (faceNodes[inode + 1] == theBetweenNode2) {
8974               nbInserted = theNodesToInsert.size();
8975
8976               // add nodes to insert
8977               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8978               for (; nIt != theNodesToInsert.end(); nIt++) {
8979                 poly_nodes.push_back(*nIt);
8980               }
8981             }
8982           }
8983           else if (faceNodes[inode] == theBetweenNode2) {
8984             if (faceNodes[inode + 1] == theBetweenNode1) {
8985               nbInserted = theNodesToInsert.size();
8986
8987               // add nodes to insert in reversed order
8988               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8989               nIt--;
8990               for (; nIt != theNodesToInsert.begin(); nIt--) {
8991                 poly_nodes.push_back(*nIt);
8992               }
8993               poly_nodes.push_back(*nIt);
8994             }
8995           }
8996           else {
8997           }
8998         }
8999       }
9000       quantities[iface] = nbFaceNodes + nbInserted;
9001     }
9002
9003     // Replace the volume
9004     SMESHDS_Mesh *aMesh = GetMeshDS();
9005
9006     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9007     {
9008       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9009       myLastCreatedElems.push_back( newElem );
9010       ReplaceElemInGroups( elem, newElem, aMesh );
9011     }
9012     aMesh->RemoveElement( elem );
9013   }
9014 }
9015
9016 namespace
9017 {
9018   //================================================================================
9019   /*!
9020    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9021    */
9022   //================================================================================
9023
9024   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9025                            vector<const SMDS_MeshNode *> & nodes,
9026                            vector<int> &                   nbNodeInFaces )
9027   {
9028     nodes.clear();
9029     nbNodeInFaces.clear();
9030     SMDS_VolumeTool vTool ( elem );
9031     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9032     {
9033       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9034       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9035       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9036     }
9037   }
9038 }
9039
9040 //=======================================================================
9041 /*!
9042  * \brief Convert elements contained in a sub-mesh to quadratic
9043  * \return int - nb of checked elements
9044  */
9045 //=======================================================================
9046
9047 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9048                                                   SMESH_MesherHelper& theHelper,
9049                                                   const bool          theForce3d)
9050 {
9051   //MESSAGE("convertElemToQuadratic");
9052   smIdType nbElem = 0;
9053   if( !theSm ) return nbElem;
9054
9055   vector<int> nbNodeInFaces;
9056   vector<const SMDS_MeshNode *> nodes;
9057   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9058   while(ElemItr->more())
9059   {
9060     nbElem++;
9061     const SMDS_MeshElement* elem = ElemItr->next();
9062     if( !elem ) continue;
9063
9064     // analyse a necessity of conversion
9065     const SMDSAbs_ElementType aType = elem->GetType();
9066     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9067       continue;
9068     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9069     bool hasCentralNodes = false;
9070     if ( elem->IsQuadratic() )
9071     {
9072       bool alreadyOK;
9073       switch ( aGeomType ) {
9074       case SMDSEntity_Quad_Triangle:
9075       case SMDSEntity_Quad_Quadrangle:
9076       case SMDSEntity_Quad_Hexa:
9077       case SMDSEntity_Quad_Penta:
9078         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9079
9080       case SMDSEntity_BiQuad_Triangle:
9081       case SMDSEntity_BiQuad_Quadrangle:
9082       case SMDSEntity_TriQuad_Hexa:
9083       case SMDSEntity_BiQuad_Penta:
9084         alreadyOK = theHelper.GetIsBiQuadratic();
9085         hasCentralNodes = true;
9086         break;
9087       default:
9088         alreadyOK = true;
9089       }
9090       // take into account already present medium nodes
9091       switch ( aType ) {
9092       case SMDSAbs_Volume:
9093         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9094       case SMDSAbs_Face:
9095         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9096       case SMDSAbs_Edge:
9097         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9098       default:;
9099       }
9100       if ( alreadyOK )
9101         continue;
9102     }
9103     // get elem data needed to re-create it
9104     //
9105     const smIdType id = elem->GetID();
9106     const int nbNodes = elem->NbCornerNodes();
9107     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9108     if ( aGeomType == SMDSEntity_Polyhedra )
9109       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9110     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9111       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9112
9113     // remove a linear element
9114     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9115
9116     // remove central nodes of biquadratic elements (biquad->quad conversion)
9117     if ( hasCentralNodes )
9118       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9119         if ( nodes[i]->NbInverseElements() == 0 )
9120           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9121
9122     const SMDS_MeshElement* NewElem = 0;
9123
9124     switch( aType )
9125     {
9126     case SMDSAbs_Edge :
9127     {
9128       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9129       break;
9130     }
9131     case SMDSAbs_Face :
9132     {
9133       switch(nbNodes)
9134       {
9135       case 3:
9136         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9137         break;
9138       case 4:
9139         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9140         break;
9141       default:
9142         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9143       }
9144       break;
9145     }
9146     case SMDSAbs_Volume :
9147     {
9148       switch( aGeomType )
9149       {
9150       case SMDSEntity_Tetra:
9151         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9152         break;
9153       case SMDSEntity_Pyramid:
9154         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9155         break;
9156       case SMDSEntity_Penta:
9157       case SMDSEntity_Quad_Penta:
9158       case SMDSEntity_BiQuad_Penta:
9159         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9160         break;
9161       case SMDSEntity_Hexa:
9162       case SMDSEntity_Quad_Hexa:
9163       case SMDSEntity_TriQuad_Hexa:
9164         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9165                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9166         break;
9167       case SMDSEntity_Hexagonal_Prism:
9168       default:
9169         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9170       }
9171       break;
9172     }
9173     default :
9174       continue;
9175     }
9176     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9177     if( NewElem && NewElem->getshapeId() < 1 )
9178       theSm->AddElement( NewElem );
9179   }
9180   return nbElem;
9181 }
9182 //=======================================================================
9183 //function : ConvertToQuadratic
9184 //purpose  :
9185 //=======================================================================
9186
9187 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9188 {
9189   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9190   SMESHDS_Mesh* meshDS = GetMeshDS();
9191
9192   SMESH_MesherHelper aHelper(*myMesh);
9193
9194   aHelper.SetIsQuadratic( true );
9195   aHelper.SetIsBiQuadratic( theToBiQuad );
9196   aHelper.SetElementsOnShape(true);
9197   aHelper.ToFixNodeParameters( true );
9198
9199   // convert elements assigned to sub-meshes
9200   smIdType nbCheckedElems = 0;
9201   if ( myMesh->HasShapeToMesh() )
9202   {
9203     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9204     {
9205       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9206       while ( smIt->more() ) {
9207         SMESH_subMesh* sm = smIt->next();
9208         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9209           aHelper.SetSubShape( sm->GetSubShape() );
9210           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9211         }
9212       }
9213     }
9214   }
9215
9216   // convert elements NOT assigned to sub-meshes
9217   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9218   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9219   {
9220     aHelper.SetElementsOnShape(false);
9221     SMESHDS_SubMesh *smDS = 0;
9222
9223     // convert edges
9224     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9225     while( aEdgeItr->more() )
9226     {
9227       const SMDS_MeshEdge* edge = aEdgeItr->next();
9228       if ( !edge->IsQuadratic() )
9229       {
9230         smIdType                  id = edge->GetID();
9231         const SMDS_MeshNode* n1 = edge->GetNode(0);
9232         const SMDS_MeshNode* n2 = edge->GetNode(1);
9233
9234         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9235
9236         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9237         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9238       }
9239       else
9240       {
9241         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9242       }
9243     }
9244
9245     // convert faces
9246     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9247     while( aFaceItr->more() )
9248     {
9249       const SMDS_MeshFace* face = aFaceItr->next();
9250       if ( !face ) continue;
9251       
9252       const SMDSAbs_EntityType type = face->GetEntityType();
9253       bool alreadyOK;
9254       switch( type )
9255       {
9256       case SMDSEntity_Quad_Triangle:
9257       case SMDSEntity_Quad_Quadrangle:
9258         alreadyOK = !theToBiQuad;
9259         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9260         break;
9261       case SMDSEntity_BiQuad_Triangle:
9262       case SMDSEntity_BiQuad_Quadrangle:
9263         alreadyOK = theToBiQuad;
9264         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9265         break;
9266       default: alreadyOK = false;
9267       }
9268       if ( alreadyOK )
9269         continue;
9270
9271       const smIdType id = face->GetID();
9272       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9273
9274       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9275
9276       SMDS_MeshFace * NewFace = 0;
9277       switch( type )
9278       {
9279       case SMDSEntity_Triangle:
9280       case SMDSEntity_Quad_Triangle:
9281       case SMDSEntity_BiQuad_Triangle:
9282         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9283         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9284           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9285         break;
9286
9287       case SMDSEntity_Quadrangle:
9288       case SMDSEntity_Quad_Quadrangle:
9289       case SMDSEntity_BiQuad_Quadrangle:
9290         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9291         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9292           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9293         break;
9294
9295       default:;
9296         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9297       }
9298       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9299     }
9300
9301     // convert volumes
9302     vector<int> nbNodeInFaces;
9303     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9304     while(aVolumeItr->more())
9305     {
9306       const SMDS_MeshVolume* volume = aVolumeItr->next();
9307       if ( !volume ) continue;
9308
9309       const SMDSAbs_EntityType type = volume->GetEntityType();
9310       if ( volume->IsQuadratic() )
9311       {
9312         bool alreadyOK;
9313         switch ( type )
9314         {
9315         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9316         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9317         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9318         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9319         default:                      alreadyOK = true;
9320         }
9321         if ( alreadyOK )
9322         {
9323           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9324           continue;
9325         }
9326       }
9327       const smIdType id = volume->GetID();
9328       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9329       if ( type == SMDSEntity_Polyhedra )
9330         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9331       else if ( type == SMDSEntity_Hexagonal_Prism )
9332         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9333
9334       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9335
9336       SMDS_MeshVolume * NewVolume = 0;
9337       switch ( type )
9338       {
9339       case SMDSEntity_Tetra:
9340         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9341         break;
9342       case SMDSEntity_Hexa:
9343       case SMDSEntity_Quad_Hexa:
9344       case SMDSEntity_TriQuad_Hexa:
9345         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9346                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9348           if ( nodes[i]->NbInverseElements() == 0 )
9349             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9350         break;
9351       case SMDSEntity_Pyramid:
9352         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9353                                       nodes[3], nodes[4], id, theForce3d);
9354         break;
9355       case SMDSEntity_Penta:
9356       case SMDSEntity_Quad_Penta:
9357       case SMDSEntity_BiQuad_Penta:
9358         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9359                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9360         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9361           if ( nodes[i]->NbInverseElements() == 0 )
9362             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9363         break;
9364       case SMDSEntity_Hexagonal_Prism:
9365       default:
9366         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9367       }
9368       ReplaceElemInGroups(volume, NewVolume, meshDS);
9369     }
9370   }
9371
9372   if ( !theForce3d )
9373   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9374     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9375     // aHelper.FixQuadraticElements(myError);
9376     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9377   }
9378 }
9379
9380 //================================================================================
9381 /*!
9382  * \brief Makes given elements quadratic
9383  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9384  *  \param theElements - elements to make quadratic
9385  */
9386 //================================================================================
9387
9388 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9389                                           TIDSortedElemSet& theElements,
9390                                           const bool        theToBiQuad)
9391 {
9392   if ( theElements.empty() ) return;
9393
9394   // we believe that all theElements are of the same type
9395   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9396
9397   // get all nodes shared by theElements
9398   TIDSortedNodeSet allNodes;
9399   TIDSortedElemSet::iterator eIt = theElements.begin();
9400   for ( ; eIt != theElements.end(); ++eIt )
9401     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9402
9403   // complete theElements with elements of lower dim whose all nodes are in allNodes
9404
9405   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9406   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9407   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9408   for ( ; nIt != allNodes.end(); ++nIt )
9409   {
9410     const SMDS_MeshNode* n = *nIt;
9411     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9412     while ( invIt->more() )
9413     {
9414       const SMDS_MeshElement*      e = invIt->next();
9415       const SMDSAbs_ElementType type = e->GetType();
9416       if ( e->IsQuadratic() )
9417       {
9418         quadAdjacentElems[ type ].insert( e );
9419
9420         bool alreadyOK;
9421         switch ( e->GetEntityType() ) {
9422         case SMDSEntity_Quad_Triangle:
9423         case SMDSEntity_Quad_Quadrangle:
9424         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9425         case SMDSEntity_BiQuad_Triangle:
9426         case SMDSEntity_BiQuad_Quadrangle:
9427         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9428         default:                           alreadyOK = true;
9429         }
9430         if ( alreadyOK )
9431           continue;
9432       }
9433       if ( type >= elemType )
9434         continue; // same type or more complex linear element
9435
9436       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9437         continue; // e is already checked
9438
9439       // check nodes
9440       bool allIn = true;
9441       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9442       while ( nodeIt->more() && allIn )
9443         allIn = allNodes.count( nodeIt->next() );
9444       if ( allIn )
9445         theElements.insert(e );
9446     }
9447   }
9448
9449   SMESH_MesherHelper helper(*myMesh);
9450   helper.SetIsQuadratic( true );
9451   helper.SetIsBiQuadratic( theToBiQuad );
9452
9453   // add links of quadratic adjacent elements to the helper
9454
9455   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9456     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9457           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9458     {
9459       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9460     }
9461   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9462     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9463           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9464     {
9465       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9466     }
9467   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9468     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9469           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9470     {
9471       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9472     }
9473
9474   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9475
9476   SMESHDS_Mesh*  meshDS = GetMeshDS();
9477   SMESHDS_SubMesh* smDS = 0;
9478   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9479   {
9480     const SMDS_MeshElement* elem = *eIt;
9481
9482     bool alreadyOK;
9483     int nbCentralNodes = 0;
9484     switch ( elem->GetEntityType() ) {
9485       // linear convertible
9486     case SMDSEntity_Edge:
9487     case SMDSEntity_Triangle:
9488     case SMDSEntity_Quadrangle:
9489     case SMDSEntity_Tetra:
9490     case SMDSEntity_Pyramid:
9491     case SMDSEntity_Hexa:
9492     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9493       // quadratic that can become bi-quadratic
9494     case SMDSEntity_Quad_Triangle:
9495     case SMDSEntity_Quad_Quadrangle:
9496     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9497       // bi-quadratic
9498     case SMDSEntity_BiQuad_Triangle:
9499     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9500     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9501       // the rest
9502     default:                           alreadyOK = true;
9503     }
9504     if ( alreadyOK ) continue;
9505
9506     const SMDSAbs_ElementType type = elem->GetType();
9507     const smIdType              id = elem->GetID();
9508     const int              nbNodes = elem->NbCornerNodes();
9509     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9510
9511     helper.SetSubShape( elem->getshapeId() );
9512
9513     if ( !smDS || !smDS->Contains( elem ))
9514       smDS = meshDS->MeshElements( elem->getshapeId() );
9515     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9516
9517     SMDS_MeshElement * newElem = 0;
9518     switch( nbNodes )
9519     {
9520     case 4: // cases for most frequently used element types go first (for optimization)
9521       if ( type == SMDSAbs_Volume )
9522         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9523       else
9524         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9525       break;
9526     case 8:
9527       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9528                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9529       break;
9530     case 3:
9531       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9532       break;
9533     case 2:
9534       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9535       break;
9536     case 5:
9537       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9538                                  nodes[4], id, theForce3d);
9539       break;
9540     case 6:
9541       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9542                                  nodes[4], nodes[5], id, theForce3d);
9543       break;
9544     default:;
9545     }
9546     ReplaceElemInGroups( elem, newElem, meshDS);
9547     if( newElem && smDS )
9548       smDS->AddElement( newElem );
9549
9550     // remove central nodes
9551     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9552       if ( nodes[i]->NbInverseElements() == 0 )
9553         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9554
9555   } // loop on theElements
9556
9557   if ( !theForce3d )
9558   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9559     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9560     // helper.FixQuadraticElements( myError );
9561     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9562   }
9563 }
9564
9565 //=======================================================================
9566 /*!
9567  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9568  * \return smIdType - nb of checked elements
9569  */
9570 //=======================================================================
9571
9572 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9573                                           SMDS_ElemIteratorPtr theItr,
9574                                           const int            /*theShapeID*/)
9575 {
9576   smIdType nbElem = 0;
9577   SMESHDS_Mesh* meshDS = GetMeshDS();
9578   ElemFeatures elemType;
9579   vector<const SMDS_MeshNode *> nodes;
9580
9581   while( theItr->more() )
9582   {
9583     const SMDS_MeshElement* elem = theItr->next();
9584     nbElem++;
9585     if( elem && elem->IsQuadratic())
9586     {
9587       // get elem data
9588       int nbCornerNodes = elem->NbCornerNodes();
9589       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9590
9591       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9592
9593       //remove a quadratic element
9594       if ( !theSm || !theSm->Contains( elem ))
9595         theSm = meshDS->MeshElements( elem->getshapeId() );
9596       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9597
9598       // remove medium nodes
9599       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9600         if ( nodes[i]->NbInverseElements() == 0 )
9601           meshDS->RemoveFreeNode( nodes[i], theSm );
9602
9603       // add a linear element
9604       nodes.resize( nbCornerNodes );
9605       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9606       ReplaceElemInGroups(elem, newElem, meshDS);
9607       if( theSm && newElem )
9608         theSm->AddElement( newElem );
9609     }
9610   }
9611   return nbElem;
9612 }
9613
9614 //=======================================================================
9615 //function : ConvertFromQuadratic
9616 //purpose  :
9617 //=======================================================================
9618
9619 bool SMESH_MeshEditor::ConvertFromQuadratic()
9620 {
9621   smIdType nbCheckedElems = 0;
9622   if ( myMesh->HasShapeToMesh() )
9623   {
9624     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9625     {
9626       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9627       while ( smIt->more() ) {
9628         SMESH_subMesh* sm = smIt->next();
9629         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9630           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9631       }
9632     }
9633   }
9634
9635   smIdType totalNbElems =
9636     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9637   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9638   {
9639     SMESHDS_SubMesh *aSM = 0;
9640     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9641   }
9642
9643   return true;
9644 }
9645
9646 namespace
9647 {
9648   //================================================================================
9649   /*!
9650    * \brief Return true if all medium nodes of the element are in the node set
9651    */
9652   //================================================================================
9653
9654   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9655   {
9656     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9657       if ( !nodeSet.count( elem->GetNode(i) ))
9658         return false;
9659     return true;
9660   }
9661 }
9662
9663 //================================================================================
9664 /*!
9665  * \brief Makes given elements linear
9666  */
9667 //================================================================================
9668
9669 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9670 {
9671   if ( theElements.empty() ) return;
9672
9673   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9674   set<smIdType> mediumNodeIDs;
9675   TIDSortedElemSet::iterator eIt = theElements.begin();
9676   for ( ; eIt != theElements.end(); ++eIt )
9677   {
9678     const SMDS_MeshElement* e = *eIt;
9679     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9680       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9681   }
9682
9683   // replace given elements by linear ones
9684   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9685   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9686
9687   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9688   // except those elements sharing medium nodes of quadratic element whose medium nodes
9689   // are not all in mediumNodeIDs
9690
9691   // get remaining medium nodes
9692   TIDSortedNodeSet mediumNodes;
9693   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9694   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9695     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9696       mediumNodes.insert( mediumNodes.end(), n );
9697
9698   // find more quadratic elements to convert
9699   TIDSortedElemSet moreElemsToConvert;
9700   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9701   for ( ; nIt != mediumNodes.end(); ++nIt )
9702   {
9703     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9704     while ( invIt->more() )
9705     {
9706       const SMDS_MeshElement* e = invIt->next();
9707       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9708       {
9709         // find a more complex element including e and
9710         // whose medium nodes are not in mediumNodes
9711         bool complexFound = false;
9712         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9713         {
9714           SMDS_ElemIteratorPtr invIt2 =
9715             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9716           while ( invIt2->more() )
9717           {
9718             const SMDS_MeshElement* eComplex = invIt2->next();
9719             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9720             {
9721               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9722               if ( nbCommonNodes == e->NbNodes())
9723               {
9724                 complexFound = true;
9725                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9726                 break;
9727               }
9728             }
9729           }
9730         }
9731         if ( !complexFound )
9732           moreElemsToConvert.insert( e );
9733       }
9734     }
9735   }
9736   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9737   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9738 }
9739
9740 //=======================================================================
9741 //function : SewSideElements
9742 //purpose  :
9743 //=======================================================================
9744
9745 SMESH_MeshEditor::Sew_Error
9746 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9747                                    TIDSortedElemSet&    theSide2,
9748                                    const SMDS_MeshNode* theFirstNode1,
9749                                    const SMDS_MeshNode* theFirstNode2,
9750                                    const SMDS_MeshNode* theSecondNode1,
9751                                    const SMDS_MeshNode* theSecondNode2)
9752 {
9753   ClearLastCreated();
9754
9755   if ( theSide1.size() != theSide2.size() )
9756     return SEW_DIFF_NB_OF_ELEMENTS;
9757
9758   Sew_Error aResult = SEW_OK;
9759   // Algo:
9760   // 1. Build set of faces representing each side
9761   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9762   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9763
9764   // =======================================================================
9765   // 1. Build set of faces representing each side:
9766   // =======================================================================
9767   // a. build set of nodes belonging to faces
9768   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9769   // c. create temporary faces representing side of volumes if correspondent
9770   //    face does not exist
9771
9772   SMESHDS_Mesh* aMesh = GetMeshDS();
9773   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9774   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9775   TIDSortedElemSet             faceSet1, faceSet2;
9776   set<const SMDS_MeshElement*> volSet1,  volSet2;
9777   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9778   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9779   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9780   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9781   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9782   int iSide, iFace, iNode;
9783
9784   list<const SMDS_MeshElement* > tempFaceList;
9785   for ( iSide = 0; iSide < 2; iSide++ ) {
9786     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9787     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9788     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9789     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9790     set<const SMDS_MeshElement*>::iterator vIt;
9791     TIDSortedElemSet::iterator eIt;
9792     set<const SMDS_MeshNode*>::iterator    nIt;
9793
9794     // check that given nodes belong to given elements
9795     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9796     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9797     int firstIndex = -1, secondIndex = -1;
9798     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9799       const SMDS_MeshElement* elem = *eIt;
9800       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9801       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9802       if ( firstIndex > -1 && secondIndex > -1 ) break;
9803     }
9804     if ( firstIndex < 0 || secondIndex < 0 ) {
9805       // we can simply return until temporary faces created
9806       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9807     }
9808
9809     // -----------------------------------------------------------
9810     // 1a. Collect nodes of existing faces
9811     //     and build set of face nodes in order to detect missing
9812     //     faces corresponding to sides of volumes
9813     // -----------------------------------------------------------
9814
9815     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9816
9817     // loop on the given element of a side
9818     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9819       //const SMDS_MeshElement* elem = *eIt;
9820       const SMDS_MeshElement* elem = *eIt;
9821       if ( elem->GetType() == SMDSAbs_Face ) {
9822         faceSet->insert( elem );
9823         set <const SMDS_MeshNode*> faceNodeSet;
9824         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9825         while ( nodeIt->more() ) {
9826           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9827           nodeSet->insert( n );
9828           faceNodeSet.insert( n );
9829         }
9830         setOfFaceNodeSet.insert( faceNodeSet );
9831       }
9832       else if ( elem->GetType() == SMDSAbs_Volume )
9833         volSet->insert( elem );
9834     }
9835     // ------------------------------------------------------------------------------
9836     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9837     // ------------------------------------------------------------------------------
9838
9839     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9840       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9841       while ( fIt->more() ) { // loop on faces sharing a node
9842         const SMDS_MeshElement* f = fIt->next();
9843         if ( faceSet->find( f ) == faceSet->end() ) {
9844           // check if all nodes are in nodeSet and
9845           // complete setOfFaceNodeSet if they are
9846           set <const SMDS_MeshNode*> faceNodeSet;
9847           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9848           bool allInSet = true;
9849           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9850             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9851             if ( nodeSet->find( n ) == nodeSet->end() )
9852               allInSet = false;
9853             else
9854               faceNodeSet.insert( n );
9855           }
9856           if ( allInSet ) {
9857             faceSet->insert( f );
9858             setOfFaceNodeSet.insert( faceNodeSet );
9859           }
9860         }
9861       }
9862     }
9863
9864     // -------------------------------------------------------------------------
9865     // 1c. Create temporary faces representing sides of volumes if correspondent
9866     //     face does not exist
9867     // -------------------------------------------------------------------------
9868
9869     if ( !volSet->empty() ) {
9870       //int nodeSetSize = nodeSet->size();
9871
9872       // loop on given volumes
9873       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9874         SMDS_VolumeTool vol (*vIt);
9875         // loop on volume faces: find free faces
9876         // --------------------------------------
9877         list<const SMDS_MeshElement* > freeFaceList;
9878         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9879           if ( !vol.IsFreeFace( iFace ))
9880             continue;
9881           // check if there is already a face with same nodes in a face set
9882           const SMDS_MeshElement* aFreeFace = 0;
9883           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9884           int nbNodes = vol.NbFaceNodes( iFace );
9885           set <const SMDS_MeshNode*> faceNodeSet;
9886           vol.GetFaceNodes( iFace, faceNodeSet );
9887           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9888           if ( isNewFace ) {
9889             // no such a face is given but it still can exist, check it
9890             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9891             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9892           }
9893           if ( !aFreeFace ) {
9894             // create a temporary face
9895             if ( nbNodes == 3 ) {
9896               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9897               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9898             }
9899             else if ( nbNodes == 4 ) {
9900               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9901               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9902             }
9903             else {
9904               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9905               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9906               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9907             }
9908             if ( aFreeFace )
9909               tempFaceList.push_back( aFreeFace );
9910           }
9911
9912           if ( aFreeFace )
9913             freeFaceList.push_back( aFreeFace );
9914
9915         } // loop on faces of a volume
9916
9917         // choose one of several free faces of a volume
9918         // --------------------------------------------
9919         if ( freeFaceList.size() > 1 ) {
9920           // choose a face having max nb of nodes shared by other elems of a side
9921           int maxNbNodes = -1;
9922           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9923           while ( fIt != freeFaceList.end() ) { // loop on free faces
9924             int nbSharedNodes = 0;
9925             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9926             while ( nodeIt->more() ) { // loop on free face nodes
9927               const SMDS_MeshNode* n =
9928                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9929               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9930               while ( invElemIt->more() ) {
9931                 const SMDS_MeshElement* e = invElemIt->next();
9932                 nbSharedNodes += faceSet->count( e );
9933                 nbSharedNodes += elemSet->count( e );
9934               }
9935             }
9936             if ( nbSharedNodes > maxNbNodes ) {
9937               maxNbNodes = nbSharedNodes;
9938               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9939             }
9940             else if ( nbSharedNodes == maxNbNodes ) {
9941               fIt++;
9942             }
9943             else {
9944               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9945             }
9946           }
9947           if ( freeFaceList.size() > 1 )
9948           {
9949             // could not choose one face, use another way
9950             // choose a face most close to the bary center of the opposite side
9951             gp_XYZ aBC( 0., 0., 0. );
9952             set <const SMDS_MeshNode*> addedNodes;
9953             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9954             eIt = elemSet2->begin();
9955             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9956               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9957               while ( nodeIt->more() ) { // loop on free face nodes
9958                 const SMDS_MeshNode* n =
9959                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960                 if ( addedNodes.insert( n ).second )
9961                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9962               }
9963             }
9964             aBC /= addedNodes.size();
9965             double minDist = DBL_MAX;
9966             fIt = freeFaceList.begin();
9967             while ( fIt != freeFaceList.end() ) { // loop on free faces
9968               double dist = 0;
9969               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9970               while ( nodeIt->more() ) { // loop on free face nodes
9971                 const SMDS_MeshNode* n =
9972                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9973                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9974                 dist += ( aBC - p ).SquareModulus();
9975               }
9976               if ( dist < minDist ) {
9977                 minDist = dist;
9978                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9979               }
9980               else
9981                 fIt = freeFaceList.erase( fIt++ );
9982             }
9983           }
9984         } // choose one of several free faces of a volume
9985
9986         if ( freeFaceList.size() == 1 ) {
9987           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9988           faceSet->insert( aFreeFace );
9989           // complete a node set with nodes of a found free face
9990           //           for ( iNode = 0; iNode < ; iNode++ )
9991           //             nodeSet->insert( fNodes[ iNode ] );
9992         }
9993
9994       } // loop on volumes of a side
9995
9996       //       // complete a set of faces if new nodes in a nodeSet appeared
9997       //       // ----------------------------------------------------------
9998       //       if ( nodeSetSize != nodeSet->size() ) {
9999       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10000       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10001       //           while ( fIt->more() ) { // loop on faces sharing a node
10002       //             const SMDS_MeshElement* f = fIt->next();
10003       //             if ( faceSet->find( f ) == faceSet->end() ) {
10004       //               // check if all nodes are in nodeSet and
10005       //               // complete setOfFaceNodeSet if they are
10006       //               set <const SMDS_MeshNode*> faceNodeSet;
10007       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10008       //               bool allInSet = true;
10009       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10010       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10011       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10012       //                   allInSet = false;
10013       //                 else
10014       //                   faceNodeSet.insert( n );
10015       //               }
10016       //               if ( allInSet ) {
10017       //                 faceSet->insert( f );
10018       //                 setOfFaceNodeSet.insert( faceNodeSet );
10019       //               }
10020       //             }
10021       //           }
10022       //         }
10023       //       }
10024     } // Create temporary faces, if there are volumes given
10025   } // loop on sides
10026
10027   if ( faceSet1.size() != faceSet2.size() ) {
10028     // delete temporary faces: they are in reverseElements of actual nodes
10029     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10030     //    while ( tmpFaceIt->more() )
10031     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10032     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10033     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10034     //      aMesh->RemoveElement(*tmpFaceIt);
10035     MESSAGE("Diff nb of faces");
10036     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10037   }
10038
10039   // ============================================================
10040   // 2. Find nodes to merge:
10041   //              bind a node to remove to a node to put instead
10042   // ============================================================
10043
10044   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10045   if ( theFirstNode1 != theFirstNode2 )
10046     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10047   if ( theSecondNode1 != theSecondNode2 )
10048     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10049
10050   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10051   set< long > linkIdSet; // links to process
10052   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10053
10054   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10055   list< NLink > linkList[2];
10056   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10057   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10058   // loop on links in linkList; find faces by links and append links
10059   // of the found faces to linkList
10060   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10061   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10062   {
10063     NLink link[] = { *linkIt[0], *linkIt[1] };
10064     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10065     if ( !linkIdSet.count( linkID ) )
10066       continue;
10067
10068     // by links, find faces in the face sets,
10069     // and find indices of link nodes in the found faces;
10070     // in a face set, there is only one or no face sharing a link
10071     // ---------------------------------------------------------------
10072
10073     const SMDS_MeshElement* face[] = { 0, 0 };
10074     vector<const SMDS_MeshNode*> fnodes[2];
10075     int iLinkNode[2][2];
10076     TIDSortedElemSet avoidSet;
10077     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10078       const SMDS_MeshNode* n1 = link[iSide].first;
10079       const SMDS_MeshNode* n2 = link[iSide].second;
10080       //cout << "Side " << iSide << " ";
10081       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10082       // find a face by two link nodes
10083       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10084                                                       *faceSetPtr[ iSide ], avoidSet,
10085                                                       &iLinkNode[iSide][0],
10086                                                       &iLinkNode[iSide][1] );
10087       if ( face[ iSide ])
10088       {
10089         //cout << " F " << face[ iSide]->GetID() <<endl;
10090         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10091         // put face nodes to fnodes
10092         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10093         fnodes[ iSide ].assign( nIt, nEnd );
10094         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10095       }
10096     }
10097
10098     // check similarity of elements of the sides
10099     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10100       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10101       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10102         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10103       }
10104       else {
10105         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10106       }
10107       break; // do not return because it's necessary to remove tmp faces
10108     }
10109
10110     // set nodes to merge
10111     // -------------------
10112
10113     if ( face[0] && face[1] )  {
10114       const int nbNodes = face[0]->NbNodes();
10115       if ( nbNodes != face[1]->NbNodes() ) {
10116         MESSAGE("Diff nb of face nodes");
10117         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10118         break; // do not return because it s necessary to remove tmp faces
10119       }
10120       bool reverse[] = { false, false }; // order of nodes in the link
10121       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10122         // analyse link orientation in faces
10123         int i1 = iLinkNode[ iSide ][ 0 ];
10124         int i2 = iLinkNode[ iSide ][ 1 ];
10125         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10126       }
10127       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10128       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10129       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10130       {
10131         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10132                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10133       }
10134
10135       // add other links of the faces to linkList
10136       // -----------------------------------------
10137
10138       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10139         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10140         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10141         if ( !iter_isnew.second ) { // already in a set: no need to process
10142           linkIdSet.erase( iter_isnew.first );
10143         }
10144         else // new in set == encountered for the first time: add
10145         {
10146           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10147           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10148           linkList[0].push_back ( NLink( n1, n2 ));
10149           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10150         }
10151       }
10152     } // 2 faces found
10153
10154     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10155       break;
10156
10157   } // loop on link lists
10158
10159   if ( aResult == SEW_OK &&
10160        ( //linkIt[0] != linkList[0].end() ||
10161         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10162     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10163              " " << (faceSetPtr[1]->empty()));
10164     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10165   }
10166
10167   // ====================================================================
10168   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10169   // ====================================================================
10170
10171   // delete temporary faces
10172   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10173   //  while ( tmpFaceIt->more() )
10174   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10175   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10176   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10177     aMesh->RemoveElement(*tmpFaceIt);
10178
10179   if ( aResult != SEW_OK)
10180     return aResult;
10181
10182   list< smIdType > nodeIDsToRemove;
10183   vector< const SMDS_MeshNode*> nodes;
10184   ElemFeatures elemType;
10185
10186   // loop on nodes replacement map
10187   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10188   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10189     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10190     {
10191       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10192       nodeIDsToRemove.push_back( nToRemove->GetID() );
10193       // loop on elements sharing nToRemove
10194       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10195       while ( invElemIt->more() ) {
10196         const SMDS_MeshElement* e = invElemIt->next();
10197         // get a new suite of nodes: make replacement
10198         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10199         nodes.resize( nbNodes );
10200         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10201         while ( nIt->more() ) {
10202           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10203           nnIt = nReplaceMap.find( n );
10204           if ( nnIt != nReplaceMap.end() ) {
10205             nbReplaced++;
10206             n = (*nnIt).second;
10207           }
10208           nodes[ i++ ] = n;
10209         }
10210         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10211         //         elemIDsToRemove.push_back( e->GetID() );
10212         //       else
10213         if ( nbReplaced )
10214         {
10215           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10216           aMesh->RemoveElement( e );
10217
10218           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10219           {
10220             AddToSameGroups( newElem, e, aMesh );
10221             if ( int aShapeId = e->getshapeId() )
10222               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10223           }
10224         }
10225       }
10226     }
10227
10228   Remove( nodeIDsToRemove, true );
10229
10230   return aResult;
10231 }
10232
10233 //================================================================================
10234 /*!
10235  * \brief Find corresponding nodes in two sets of faces
10236  * \param theSide1 - first face set
10237  * \param theSide2 - second first face
10238  * \param theFirstNode1 - a boundary node of set 1
10239  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10240  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10241  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10242  * \param nReplaceMap - output map of corresponding nodes
10243  * \return bool  - is a success or not
10244  */
10245 //================================================================================
10246
10247 #ifdef _DEBUG_
10248 //#define DEBUG_MATCHING_NODES
10249 #endif
10250
10251 SMESH_MeshEditor::Sew_Error
10252 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10253                                     set<const SMDS_MeshElement*>& theSide2,
10254                                     const SMDS_MeshNode*          theFirstNode1,
10255                                     const SMDS_MeshNode*          theFirstNode2,
10256                                     const SMDS_MeshNode*          theSecondNode1,
10257                                     const SMDS_MeshNode*          theSecondNode2,
10258                                     TNodeNodeMap &                nReplaceMap)
10259 {
10260   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10261
10262   nReplaceMap.clear();
10263   //if ( theFirstNode1 != theFirstNode2 )
10264   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10265   //if ( theSecondNode1 != theSecondNode2 )
10266   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10267
10268   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10269   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10270
10271   list< NLink > linkList[2];
10272   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10273   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10274
10275   // loop on links in linkList; find faces by links and append links
10276   // of the found faces to linkList
10277   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10278   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10279     NLink link[] = { *linkIt[0], *linkIt[1] };
10280     if ( linkSet.find( link[0] ) == linkSet.end() )
10281       continue;
10282
10283     // by links, find faces in the face sets,
10284     // and find indices of link nodes in the found faces;
10285     // in a face set, there is only one or no face sharing a link
10286     // ---------------------------------------------------------------
10287
10288     const SMDS_MeshElement* face[] = { 0, 0 };
10289     list<const SMDS_MeshNode*> notLinkNodes[2];
10290     //bool reverse[] = { false, false }; // order of notLinkNodes
10291     int nbNodes[2];
10292     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10293     {
10294       const SMDS_MeshNode* n1 = link[iSide].first;
10295       const SMDS_MeshNode* n2 = link[iSide].second;
10296       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10297       set< const SMDS_MeshElement* > facesOfNode1;
10298       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10299       {
10300         // during a loop of the first node, we find all faces around n1,
10301         // during a loop of the second node, we find one face sharing both n1 and n2
10302         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10303         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10304         while ( fIt->more() ) { // loop on faces sharing a node
10305           const SMDS_MeshElement* f = fIt->next();
10306           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10307               ! facesOfNode1.insert( f ).second ) // f encounters twice
10308           {
10309             if ( face[ iSide ] ) {
10310               MESSAGE( "2 faces per link " );
10311               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10312             }
10313             face[ iSide ] = f;
10314             faceSet->erase( f );
10315
10316             // get not link nodes
10317             int nbN = f->NbNodes();
10318             if ( f->IsQuadratic() )
10319               nbN /= 2;
10320             nbNodes[ iSide ] = nbN;
10321             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10322             int i1 = f->GetNodeIndex( n1 );
10323             int i2 = f->GetNodeIndex( n2 );
10324             int iEnd = nbN, iBeg = -1, iDelta = 1;
10325             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10326             if ( reverse ) {
10327               std::swap( iEnd, iBeg ); iDelta = -1;
10328             }
10329             int i = i2;
10330             while ( true ) {
10331               i += iDelta;
10332               if ( i == iEnd ) i = iBeg + iDelta;
10333               if ( i == i1 ) break;
10334               nodes.push_back ( f->GetNode( i ) );
10335             }
10336           }
10337         }
10338       }
10339     }
10340     // check similarity of elements of the sides
10341     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10342       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10343       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10344         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10345       }
10346       else {
10347         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10348       }
10349     }
10350
10351     // set nodes to merge
10352     // -------------------
10353
10354     if ( face[0] && face[1] )  {
10355       if ( nbNodes[0] != nbNodes[1] ) {
10356         MESSAGE("Diff nb of face nodes");
10357         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10358       }
10359 #ifdef DEBUG_MATCHING_NODES
10360       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10361                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10362                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10363 #endif
10364       int nbN = nbNodes[0];
10365       {
10366         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10367         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10368         for ( int i = 0 ; i < nbN - 2; ++i ) {
10369 #ifdef DEBUG_MATCHING_NODES
10370           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10371 #endif
10372           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10373         }
10374       }
10375
10376       // add other links of the face 1 to linkList
10377       // -----------------------------------------
10378
10379       const SMDS_MeshElement* f0 = face[0];
10380       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10381       for ( int i = 0; i < nbN; i++ )
10382       {
10383         const SMDS_MeshNode* n2 = f0->GetNode( i );
10384         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10385           linkSet.insert( SMESH_TLink( n1, n2 ));
10386         if ( !iter_isnew.second ) { // already in a set: no need to process
10387           linkSet.erase( iter_isnew.first );
10388         }
10389         else // new in set == encountered for the first time: add
10390         {
10391 #ifdef DEBUG_MATCHING_NODES
10392           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10393                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10394 #endif
10395           linkList[0].push_back ( NLink( n1, n2 ));
10396           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10397         }
10398         n1 = n2;
10399       }
10400     } // 2 faces found
10401   } // loop on link lists
10402
10403   return SEW_OK;
10404 }
10405
10406 namespace // automatically find theAffectedElems for DoubleNodes()
10407 {
10408   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10409
10410   //--------------------------------------------------------------------------------
10411   // Nodes shared by adjacent FissureBorder's.
10412   // 1 node  if FissureBorder separates faces
10413   // 2 nodes if FissureBorder separates volumes
10414   struct SubBorder
10415   {
10416     const SMDS_MeshNode* _nodes[2];
10417     int                  _nbNodes;
10418
10419     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10420     {
10421       _nodes[0] = n1;
10422       _nodes[1] = n2;
10423       _nbNodes = bool( n1 ) + bool( n2 );
10424       if ( _nbNodes == 2 && n1 > n2 )
10425         std::swap( _nodes[0], _nodes[1] );
10426     }
10427     bool operator<( const SubBorder& other ) const
10428     {
10429       for ( int i = 0; i < _nbNodes; ++i )
10430       {
10431         if ( _nodes[i] < other._nodes[i] ) return true;
10432         if ( _nodes[i] > other._nodes[i] ) return false;
10433       }
10434       return false;
10435     }
10436   };
10437
10438   //--------------------------------------------------------------------------------
10439   // Map a SubBorder to all FissureBorder it bounds
10440   struct FissureBorder;
10441   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10442   typedef TBorderLinks::iterator                               TMappedSub;
10443
10444   //--------------------------------------------------------------------------------
10445   /*!
10446    * \brief Element border (volume facet or face edge) at a fissure
10447    */
10448   struct FissureBorder
10449   {
10450     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10451     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10452
10453     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10454     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10455
10456     FissureBorder( FissureBorder && from ) // move constructor
10457     {
10458       std::swap( _nodes,       from._nodes );
10459       std::swap( _sortedNodes, from._sortedNodes );
10460       _elems[0] = from._elems[0];
10461       _elems[1] = from._elems[1];
10462     }
10463
10464     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10465                    std::vector< const SMDS_MeshElement* > & adjElems)
10466       : _nodes( elemToDuplicate->NbCornerNodes() )
10467     {
10468       for ( size_t i = 0; i < _nodes.size(); ++i )
10469         _nodes[i] = elemToDuplicate->GetNode( i );
10470
10471       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10472       findAdjacent( type, adjElems );
10473     }
10474
10475     FissureBorder( const SMDS_MeshNode**                    nodes,
10476                    const size_t                             nbNodes,
10477                    const SMDSAbs_ElementType                adjElemsType,
10478                    std::vector< const SMDS_MeshElement* > & adjElems)
10479       : _nodes( nodes, nodes + nbNodes )
10480     {
10481       findAdjacent( adjElemsType, adjElems );
10482     }
10483
10484     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10485                        std::vector< const SMDS_MeshElement* > & adjElems)
10486     {
10487       _elems[0] = _elems[1] = 0;
10488       adjElems.clear();
10489       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10490         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10491           _elems[i] = adjElems[i];
10492     }
10493
10494     bool operator<( const FissureBorder& other ) const
10495     {
10496       return GetSortedNodes() < other.GetSortedNodes();
10497     }
10498
10499     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10500     {
10501       if ( _sortedNodes.empty() && !_nodes.empty() )
10502       {
10503         FissureBorder* me = const_cast<FissureBorder*>( this );
10504         me->_sortedNodes = me->_nodes;
10505         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10506       }
10507       return _sortedNodes;
10508     }
10509
10510     size_t NbSub() const
10511     {
10512       return _nodes.size();
10513     }
10514
10515     SubBorder Sub(size_t i) const
10516     {
10517       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10518     }
10519
10520     void AddSelfTo( TBorderLinks& borderLinks )
10521     {
10522       _mappedSubs.resize( NbSub() );
10523       for ( size_t i = 0; i < NbSub(); ++i )
10524       {
10525         TBorderLinks::iterator s2b =
10526           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10527         s2b->second.push_back( this );
10528         _mappedSubs[ i ] = s2b;
10529       }
10530     }
10531
10532     void Clear()
10533     {
10534       _nodes.clear();
10535     }
10536
10537     const SMDS_MeshElement* GetMarkedElem() const
10538     {
10539       if ( _nodes.empty() ) return 0; // cleared
10540       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10541       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10542       return 0;
10543     }
10544
10545     gp_XYZ GetNorm() const // normal to the border
10546     {
10547       gp_XYZ norm;
10548       if ( _nodes.size() == 2 )
10549       {
10550         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10551         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10552           avgNorm += norm;
10553         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10554           avgNorm += norm;
10555
10556         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10557         norm = bordDir ^ avgNorm;
10558       }
10559       else
10560       {
10561         SMESH_NodeXYZ p0( _nodes[0] );
10562         SMESH_NodeXYZ p1( _nodes[1] );
10563         SMESH_NodeXYZ p2( _nodes[2] );
10564         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10565       }
10566       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10567         norm.Reverse();
10568
10569       return norm;
10570     }
10571
10572     void ChooseSide() // mark an _elem located at positive side of fissure
10573     {
10574       _elems[0]->setIsMarked( true );
10575       gp_XYZ norm = GetNorm();
10576       double maxX = norm.Coord(1);
10577       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10578       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10579       if ( maxX < 0 )
10580       {
10581         _elems[0]->setIsMarked( false );
10582         if ( _elems[1] )
10583           _elems[1]->setIsMarked( true );
10584       }
10585     }
10586
10587   }; // struct FissureBorder
10588
10589   //--------------------------------------------------------------------------------
10590   /*!
10591    * \brief Classifier of elements at fissure edge
10592    */
10593   class FissureNormal
10594   {
10595     std::vector< gp_XYZ > _normals;
10596     bool                  _bothIn;
10597
10598   public:
10599     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10600     {
10601       _bothIn = false;
10602       _normals.reserve(2);
10603       _normals.push_back( bord.GetNorm() );
10604       if ( _normals.size() == 2 )
10605         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10606     }
10607
10608     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10609     {
10610       bool isIn = false;
10611       switch ( _normals.size() ) {
10612       case 1:
10613       {
10614         isIn = !isOut( n, _normals[0], elem );
10615         break;
10616       }
10617       case 2:
10618       {
10619         bool in1 = !isOut( n, _normals[0], elem );
10620         bool in2 = !isOut( n, _normals[1], elem );
10621         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10622       }
10623       }
10624       return isIn;
10625     }
10626   };
10627
10628   //================================================================================
10629   /*!
10630    * \brief Classify an element by a plane passing through a node
10631    */
10632   //================================================================================
10633
10634   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10635   {
10636     SMESH_NodeXYZ p = n;
10637     double sumDot = 0;
10638     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10639     {
10640       SMESH_NodeXYZ pi = elem->GetNode( i );
10641       sumDot += norm * ( pi - p );
10642     }
10643     return sumDot < -1e-100;
10644   }
10645
10646   //================================================================================
10647   /*!
10648    * \brief Find FissureBorder's by nodes to duplicate
10649    */
10650   //================================================================================
10651
10652   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10653                            std::vector< FissureBorder > & theFissureBorders )
10654   {
10655     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10656     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10657     if ( !n ) return;
10658     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10659     if ( n->NbInverseElements( elemType ) == 0 )
10660     {
10661       elemType = SMDSAbs_Face;
10662       if ( n->NbInverseElements( elemType ) == 0 )
10663         return;
10664     }
10665     // unmark elements touching the fissure
10666     for ( ; nIt != theNodes.end(); ++nIt )
10667       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10668
10669     // loop on elements touching the fissure to get their borders belonging to the fissure
10670     std::set< FissureBorder >              fissureBorders;
10671     std::vector< const SMDS_MeshElement* > adjElems;
10672     std::vector< const SMDS_MeshNode* >    nodes;
10673     SMDS_VolumeTool volTool;
10674     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10675     {
10676       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10677       while ( invIt->more() )
10678       {
10679         const SMDS_MeshElement* eInv = invIt->next();
10680         if ( eInv->isMarked() ) continue;
10681         eInv->setIsMarked( true );
10682
10683         if ( elemType == SMDSAbs_Volume )
10684         {
10685           volTool.Set( eInv );
10686           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10687           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10688           {
10689             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10690             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10691             nodes.clear();
10692             bool allOnFissure = true;
10693             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10694               if (( allOnFissure = theNodes.count( nn[ iN ])))
10695                 nodes.push_back( nn[ iN ]);
10696             if ( allOnFissure )
10697               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10698                                                                elemType, adjElems )));
10699           }
10700         }
10701         else // elemType == SMDSAbs_Face
10702         {
10703           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10704           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10705           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10706           {
10707             nn[1]      = eInv->GetNode( iN );
10708             onFissure1 = theNodes.count( nn[1] );
10709             if ( onFissure0 && onFissure1 )
10710               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10711             nn[0]      = nn[1];
10712             onFissure0 = onFissure1;
10713           }
10714         }
10715       }
10716     }
10717
10718     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10719     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10720     for ( ; bord != fissureBorders.end(); ++bord )
10721     {
10722       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10723     }
10724     return;
10725   } // findFissureBorders()
10726
10727   //================================================================================
10728   /*!
10729    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10730    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10731    *  \param [in] theNodesNot - nodes not to duplicate
10732    *  \param [out] theAffectedElems - the found elements
10733    */
10734   //================================================================================
10735
10736   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10737                           TIDSortedElemSet&       theAffectedElems)
10738   {
10739     if ( theElemsOrNodes.empty() ) return;
10740
10741     // find FissureBorder's
10742
10743     std::vector< FissureBorder >           fissure;
10744     std::vector< const SMDS_MeshElement* > elemsByFacet;
10745
10746     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10747     if ( (*elIt)->GetType() == SMDSAbs_Node )
10748     {
10749       findFissureBorders( theElemsOrNodes, fissure );
10750     }
10751     else
10752     {
10753       fissure.reserve( theElemsOrNodes.size() );
10754       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10755       {
10756         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10757         if ( !fissure.back()._elems[1] )
10758           fissure.pop_back();
10759       }
10760     }
10761     if ( fissure.empty() )
10762       return;
10763
10764     // fill borderLinks
10765
10766     TBorderLinks borderLinks;
10767
10768     for ( size_t i = 0; i < fissure.size(); ++i )
10769     {
10770       fissure[i].AddSelfTo( borderLinks );
10771     }
10772
10773     // get theAffectedElems
10774
10775     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10776     for ( size_t i = 0; i < fissure.size(); ++i )
10777       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10778       {
10779         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10780                                         false, /*markElem=*/true );
10781       }
10782
10783     std::vector<const SMDS_MeshNode *>                 facetNodes;
10784     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10785     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10786
10787     // choose a side of fissure
10788     fissure[0].ChooseSide();
10789     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10790
10791     size_t nbCheckedBorders = 0;
10792     while ( nbCheckedBorders < fissure.size() )
10793     {
10794       // find a FissureBorder to treat
10795       FissureBorder* bord = 0;
10796       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10797         if ( fissure[i].GetMarkedElem() )
10798           bord = & fissure[i];
10799       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10800         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10801         {
10802           bord = & fissure[i];
10803           bord->ChooseSide();
10804           theAffectedElems.insert( bord->GetMarkedElem() );
10805         }
10806       if ( !bord ) return;
10807       ++nbCheckedBorders;
10808
10809       // treat FissureBorder's linked to bord
10810       fissureNodes.clear();
10811       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10812       for ( size_t i = 0; i < bord->NbSub(); ++i )
10813       {
10814         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10815         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10816         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10817         const SubBorder&                          sb = l2b->first;
10818         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10819
10820         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10821         {
10822           for ( int j = 0; j < sb._nbNodes; ++j )
10823             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10824           continue;
10825         }
10826
10827         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10828         // until an elem adjacent to a neighbour FissureBorder is found
10829         facetNodes.clear();
10830         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10831         facetNodes.resize( sb._nbNodes + 1 );
10832
10833         while ( bordElem )
10834         {
10835           // check if bordElem is adjacent to a neighbour FissureBorder
10836           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10837           {
10838             FissureBorder* bord2 = linkedBorders[j];
10839             if ( bord2 == bord ) continue;
10840             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10841               bordElem = 0;
10842             else
10843               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10844           }
10845           if ( !bordElem )
10846             break;
10847
10848           // find the next bordElem
10849           const SMDS_MeshElement* nextBordElem = 0;
10850           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10851           {
10852             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10853             if ( fissureNodes.count( n )) continue;
10854
10855             facetNodes[ sb._nbNodes ] = n;
10856             elemsByFacet.clear();
10857             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10858             {
10859               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10860                 if ( elemsByFacet[ iE ] != bordElem &&
10861                      !elemsByFacet[ iE ]->isMarked() )
10862                 {
10863                   theAffectedElems.insert( elemsByFacet[ iE ]);
10864                   elemsByFacet[ iE ]->setIsMarked( true );
10865                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10866                     nextBordElem = elemsByFacet[ iE ];
10867                 }
10868             }
10869           }
10870           bordElem = nextBordElem;
10871
10872         } // while ( bordElem )
10873
10874         linkedBorders.clear(); // not to treat this link any more
10875
10876       } // loop on SubBorder's of a FissureBorder
10877
10878       bord->Clear();
10879
10880     } // loop on FissureBorder's
10881
10882
10883     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10884
10885     // mark nodes of theAffectedElems
10886     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10887
10888     // unmark nodes of the fissure
10889     elIt = theElemsOrNodes.begin();
10890     if ( (*elIt)->GetType() == SMDSAbs_Node )
10891       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10892     else
10893       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10894
10895     std::vector< gp_XYZ > normVec;
10896
10897     // loop on nodes of the fissure, add elements having marked nodes
10898     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10899     {
10900       const SMDS_MeshElement* e = (*elIt);
10901       if ( e->GetType() != SMDSAbs_Node )
10902         e->setIsMarked( true ); // avoid adding a fissure element
10903
10904       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10905       {
10906         const SMDS_MeshNode* n = e->GetNode( iN );
10907         if ( fissEdgeNodes2Norm.count( n ))
10908           continue;
10909
10910         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10911         while ( invIt->more() )
10912         {
10913           const SMDS_MeshElement* eInv = invIt->next();
10914           if ( eInv->isMarked() ) continue;
10915           eInv->setIsMarked( true );
10916
10917           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10918           while( nIt->more() )
10919             if ( nIt->next()->isMarked())
10920             {
10921               theAffectedElems.insert( eInv );
10922               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10923               n->setIsMarked( false );
10924               break;
10925             }
10926         }
10927       }
10928     }
10929
10930     // add elements on the fissure edge
10931     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10932     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10933     {
10934       const SMDS_MeshNode* edgeNode = n2N->first;
10935       const FissureNormal & normals = n2N->second;
10936
10937       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10938       while ( invIt->more() )
10939       {
10940         const SMDS_MeshElement* eInv = invIt->next();
10941         if ( eInv->isMarked() ) continue;
10942         eInv->setIsMarked( true );
10943
10944         // classify eInv using normals
10945         bool toAdd = normals.IsIn( edgeNode, eInv );
10946         if ( toAdd ) // check if all nodes lie on the fissure edge
10947         {
10948           bool notOnEdge = false;
10949           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10950             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10951           toAdd = notOnEdge;
10952         }
10953         if ( toAdd )
10954         {
10955           theAffectedElems.insert( eInv );
10956         }
10957       }
10958     }
10959
10960     return;
10961   } // findAffectedElems()
10962 } // namespace
10963
10964 //================================================================================
10965 /*!
10966  * \brief Create elements equal (on same nodes) to given ones
10967  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10968  *              elements of the uppest dimension are duplicated.
10969  */
10970 //================================================================================
10971
10972 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10973 {
10974   ClearLastCreated();
10975   SMESHDS_Mesh* mesh = GetMeshDS();
10976
10977   // get an element type and an iterator over elements
10978
10979   SMDSAbs_ElementType type = SMDSAbs_All;
10980   SMDS_ElemIteratorPtr elemIt;
10981   if ( theElements.empty() )
10982   {
10983     if ( mesh->NbNodes() == 0 )
10984       return;
10985     // get most complex type
10986     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10987       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10988       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10989     };
10990     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10991       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10992       {
10993         type = types[i];
10994         elemIt = mesh->elementsIterator( type );
10995         break;
10996       }
10997   }
10998   else
10999   {
11000     //type = (*theElements.begin())->GetType();
11001     elemIt = SMESHUtils::elemSetIterator( theElements );
11002   }
11003
11004   // un-mark all elements to avoid duplicating just created elements
11005   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11006
11007   // duplicate elements
11008
11009   ElemFeatures elemType;
11010
11011   vector< const SMDS_MeshNode* > nodes;
11012   while ( elemIt->more() )
11013   {
11014     const SMDS_MeshElement* elem = elemIt->next();
11015     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11016         ( elem->isMarked() ))
11017       continue;
11018
11019     elemType.Init( elem, /*basicOnly=*/false );
11020     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11021
11022     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11023       newElem->setIsMarked( true );
11024   }
11025 }
11026
11027 //================================================================================
11028 /*!
11029   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11030   \param theElems - the list of elements (edges or faces) to be replicated
11031   The nodes for duplication could be found from these elements
11032   \param theNodesNot - list of nodes to NOT replicate
11033   \param theAffectedElems - the list of elements (cells and edges) to which the
11034   replicated nodes should be associated to.
11035   \return TRUE if operation has been completed successfully, FALSE otherwise
11036 */
11037 //================================================================================
11038
11039 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11040                                     const TIDSortedElemSet& theNodesNot,
11041                                     const TIDSortedElemSet& theAffectedElems )
11042 {
11043   ClearLastCreated();
11044
11045   if ( theElems.size() == 0 )
11046     return false;
11047
11048   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11049   if ( !aMeshDS )
11050     return false;
11051
11052   bool res = false;
11053   TNodeNodeMap anOldNodeToNewNode;
11054   // duplicate elements and nodes
11055   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11056   // replce nodes by duplications
11057   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11058   return res;
11059 }
11060
11061 //================================================================================
11062 /*!
11063   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11064   \param theMeshDS - mesh instance
11065   \param theElems - the elements replicated or modified (nodes should be changed)
11066   \param theNodesNot - nodes to NOT replicate
11067   \param theNodeNodeMap - relation of old node to new created node
11068   \param theIsDoubleElem - flag os to replicate element or modify
11069   \return TRUE if operation has been completed successfully, FALSE otherwise
11070 */
11071 //================================================================================
11072
11073 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11074                                    const TIDSortedElemSet& theElems,
11075                                    const TIDSortedElemSet& theNodesNot,
11076                                    TNodeNodeMap&           theNodeNodeMap,
11077                                    const bool              theIsDoubleElem )
11078 {
11079   // iterate through element and duplicate them (by nodes duplication)
11080   bool res = false;
11081   std::vector<const SMDS_MeshNode*> newNodes;
11082   ElemFeatures elemType;
11083
11084   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11085   for ( ;  elemItr != theElems.end(); ++elemItr )
11086   {
11087     const SMDS_MeshElement* anElem = *elemItr;
11088     // if (!anElem)
11089     //   continue;
11090
11091     // duplicate nodes to duplicate element
11092     bool isDuplicate = false;
11093     newNodes.resize( anElem->NbNodes() );
11094     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11095     int ind = 0;
11096     while ( anIter->more() )
11097     {
11098       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11099       const SMDS_MeshNode*  aNewNode = aCurrNode;
11100       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11101       if ( n2n != theNodeNodeMap.end() )
11102       {
11103         aNewNode = n2n->second;
11104       }
11105       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11106       {
11107         // duplicate node
11108         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11109         copyPosition( aCurrNode, aNewNode );
11110         theNodeNodeMap[ aCurrNode ] = aNewNode;
11111         myLastCreatedNodes.push_back( aNewNode );
11112       }
11113       isDuplicate |= (aCurrNode != aNewNode);
11114       newNodes[ ind++ ] = aNewNode;
11115     }
11116     if ( !isDuplicate )
11117       continue;
11118
11119     if ( theIsDoubleElem )
11120       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11121     else
11122     {
11123       // change element nodes
11124       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11125       if ( geomType == SMDSEntity_Polyhedra )
11126       {
11127         // special treatment for polyhedron
11128         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11129         if (!aPolyhedron) {
11130           MESSAGE("Warning: bad volumic element");
11131           return false;
11132         }
11133         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11134       }
11135       else
11136         // standard entity type
11137         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11138     }
11139
11140     res = true;
11141   }
11142   return res;
11143 }
11144
11145 //================================================================================
11146 /*!
11147   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11148   \param theNodes - identifiers of nodes to be doubled
11149   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11150   nodes. If list of element identifiers is empty then nodes are doubled but
11151   they not assigned to elements
11152   \return TRUE if operation has been completed successfully, FALSE otherwise
11153 */
11154 //================================================================================
11155
11156 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11157                                     const std::list< int >& theListOfModifiedElems )
11158 {
11159   ClearLastCreated();
11160
11161   if ( theListOfNodes.size() == 0 )
11162     return false;
11163
11164   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11165   if ( !aMeshDS )
11166     return false;
11167
11168   // iterate through nodes and duplicate them
11169
11170   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11171
11172   std::list< int >::const_iterator aNodeIter;
11173   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11174   {
11175     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11176     if ( !aNode )
11177       continue;
11178
11179     // duplicate node
11180
11181     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11182     if ( aNewNode )
11183     {
11184       copyPosition( aNode, aNewNode );
11185       anOldNodeToNewNode[ aNode ] = aNewNode;
11186       myLastCreatedNodes.push_back( aNewNode );
11187     }
11188   }
11189
11190   // Change nodes of elements
11191
11192   std::vector<const SMDS_MeshNode*> aNodeArr;
11193
11194   std::list< int >::const_iterator anElemIter;
11195   for ( anElemIter =  theListOfModifiedElems.begin();
11196         anElemIter != theListOfModifiedElems.end();
11197         anElemIter++ )
11198   {
11199     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11200     if ( !anElem )
11201       continue;
11202
11203     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11204     for( size_t i = 0; i < aNodeArr.size(); ++i )
11205     {
11206       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11207         anOldNodeToNewNode.find( aNodeArr[ i ]);
11208       if ( n2n != anOldNodeToNewNode.end() )
11209         aNodeArr[ i ] = n2n->second;
11210     }
11211     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11212   }
11213
11214   return true;
11215 }
11216
11217 namespace {
11218
11219   //================================================================================
11220   /*!
11221     \brief Check if element located inside shape
11222     \return TRUE if IN or ON shape, FALSE otherwise
11223   */
11224   //================================================================================
11225
11226   template<class Classifier>
11227   bool isInside(const SMDS_MeshElement* theElem,
11228                 Classifier&             theClassifier,
11229                 const double            theTol)
11230   {
11231     gp_XYZ centerXYZ (0, 0, 0);
11232     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11233       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11234
11235     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11236     theClassifier.Perform(aPnt, theTol);
11237     TopAbs_State aState = theClassifier.State();
11238     return (aState == TopAbs_IN || aState == TopAbs_ON );
11239   }
11240
11241   //================================================================================
11242   /*!
11243    * \brief Classifier of the 3D point on the TopoDS_Face
11244    *        with interaface suitable for isInside()
11245    */
11246   //================================================================================
11247
11248   struct _FaceClassifier
11249   {
11250     Extrema_ExtPS       _extremum;
11251     BRepAdaptor_Surface _surface;
11252     TopAbs_State        _state;
11253
11254     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11255     {
11256       _extremum.Initialize( _surface,
11257                             _surface.FirstUParameter(), _surface.LastUParameter(),
11258                             _surface.FirstVParameter(), _surface.LastVParameter(),
11259                             _surface.Tolerance(), _surface.Tolerance() );
11260     }
11261     void Perform(const gp_Pnt& aPnt, double theTol)
11262     {
11263       theTol *= theTol;
11264       _state = TopAbs_OUT;
11265       _extremum.Perform(aPnt);
11266       if ( _extremum.IsDone() )
11267         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11268           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11269     }
11270     TopAbs_State State() const
11271     {
11272       return _state;
11273     }
11274   };
11275 }
11276
11277 //================================================================================
11278 /*!
11279   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11280   This method is the first step of DoubleNodeElemGroupsInRegion.
11281   \param theElems - list of groups of elements (edges or faces) to be replicated
11282   \param theNodesNot - list of groups of nodes not to replicate
11283   \param theShape - shape to detect affected elements (element which geometric center
11284          located on or inside shape). If the shape is null, detection is done on faces orientations
11285          (select elements with a gravity center on the side given by faces normals).
11286          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11287          The replicated nodes should be associated to affected elements.
11288   \return true
11289   \sa DoubleNodeElemGroupsInRegion()
11290 */
11291 //================================================================================
11292
11293 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11294                                                    const TIDSortedElemSet& theNodesNot,
11295                                                    const TopoDS_Shape&     theShape,
11296                                                    TIDSortedElemSet&       theAffectedElems)
11297 {
11298   if ( theShape.IsNull() )
11299   {
11300     findAffectedElems( theElems, theAffectedElems );
11301   }
11302   else
11303   {
11304     const double aTol = Precision::Confusion();
11305     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11306     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11307     if ( theShape.ShapeType() == TopAbs_SOLID )
11308     {
11309       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11310       bsc3d->PerformInfinitePoint(aTol);
11311     }
11312     else if (theShape.ShapeType() == TopAbs_FACE )
11313     {
11314       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11315     }
11316
11317     // iterates on indicated elements and get elements by back references from their nodes
11318     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11319     for ( ;  elemItr != theElems.end(); ++elemItr )
11320     {
11321       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11322       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11323       while ( nodeItr->more() )
11324       {
11325         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11326         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11327           continue;
11328         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11329         while ( backElemItr->more() )
11330         {
11331           const SMDS_MeshElement* curElem = backElemItr->next();
11332           if ( curElem && theElems.find(curElem) == theElems.end() &&
11333                ( bsc3d.get() ?
11334                  isInside( curElem, *bsc3d, aTol ) :
11335                  isInside( curElem, *aFaceClassifier, aTol )))
11336             theAffectedElems.insert( curElem );
11337         }
11338       }
11339     }
11340   }
11341   return true;
11342 }
11343
11344 //================================================================================
11345 /*!
11346   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11347   \param theElems - group of of elements (edges or faces) to be replicated
11348   \param theNodesNot - group of nodes not to replicate
11349   \param theShape - shape to detect affected elements (element which geometric center
11350   located on or inside shape).
11351   The replicated nodes should be associated to affected elements.
11352   \return TRUE if operation has been completed successfully, FALSE otherwise
11353 */
11354 //================================================================================
11355
11356 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11357                                             const TIDSortedElemSet& theNodesNot,
11358                                             const TopoDS_Shape&     theShape )
11359 {
11360   if ( theShape.IsNull() )
11361     return false;
11362
11363   const double aTol = Precision::Confusion();
11364   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11365   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11366   if ( theShape.ShapeType() == TopAbs_SOLID )
11367   {
11368     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11369     bsc3d->PerformInfinitePoint(aTol);
11370   }
11371   else if (theShape.ShapeType() == TopAbs_FACE )
11372   {
11373     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11374   }
11375
11376   // iterates on indicated elements and get elements by back references from their nodes
11377   TIDSortedElemSet anAffected;
11378   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11379   for ( ;  elemItr != theElems.end(); ++elemItr )
11380   {
11381     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11382     if (!anElem)
11383       continue;
11384
11385     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11386     while ( nodeItr->more() )
11387     {
11388       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11389       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11390         continue;
11391       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11392       while ( backElemItr->more() )
11393       {
11394         const SMDS_MeshElement* curElem = backElemItr->next();
11395         if ( curElem && theElems.find(curElem) == theElems.end() &&
11396              ( bsc3d ?
11397                isInside( curElem, *bsc3d, aTol ) :
11398                isInside( curElem, *aFaceClassifier, aTol )))
11399           anAffected.insert( curElem );
11400       }
11401     }
11402   }
11403   return DoubleNodes( theElems, theNodesNot, anAffected );
11404 }
11405
11406 /*!
11407  *  \brief compute an oriented angle between two planes defined by four points.
11408  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11409  *  @param p0 base of the rotation axe
11410  *  @param p1 extremity of the rotation axe
11411  *  @param g1 belongs to the first plane
11412  *  @param g2 belongs to the second plane
11413  */
11414 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11415 {
11416   gp_Vec vref(p0, p1);
11417   gp_Vec v1(p0, g1);
11418   gp_Vec v2(p0, g2);
11419   gp_Vec n1 = vref.Crossed(v1);
11420   gp_Vec n2 = vref.Crossed(v2);
11421   try {
11422     return n2.AngleWithRef(n1, vref);
11423   }
11424   catch ( Standard_Failure& ) {
11425   }
11426   return Max( v1.Magnitude(), v2.Magnitude() );
11427 }
11428
11429 /*!
11430  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11431  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11432  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11433  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11434  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11435  * 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.
11436  * 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.
11437  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11438  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11439  * \param theElems - list of groups of volumes, where a group of volume is a set of
11440  *        SMDS_MeshElements sorted by Id.
11441  * \param createJointElems - if TRUE, create the elements
11442  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11443  *        the boundary between \a theDomains and the rest mesh
11444  * \return TRUE if operation has been completed successfully, FALSE otherwise
11445  */
11446 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11447                                                      bool                                 createJointElems,
11448                                                      bool                                 onAllBoundaries)
11449 {
11450   // MESSAGE("----------------------------------------------");
11451   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11452   // MESSAGE("----------------------------------------------");
11453
11454   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11455   meshDS->BuildDownWardConnectivity(true);
11456   CHRONO(50);
11457   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11458
11459   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11460   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11461   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11462
11463   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11464   std::map<int,int> celldom; // cell vtkId --> domain
11465   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11466   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11467
11468   //MESSAGE(".. Number of domains :"<<theElems.size());
11469
11470   TIDSortedElemSet theRestDomElems;
11471   const int iRestDom  = -1;
11472   const int idom0     = onAllBoundaries ? iRestDom : 0;
11473   const int nbDomains = theElems.size();
11474
11475   // Check if the domains do not share an element
11476   for (int idom = 0; idom < nbDomains-1; idom++)
11477   {
11478     //       MESSAGE("... Check of domain #" << idom);
11479     const TIDSortedElemSet& domain = theElems[idom];
11480     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11481     for (; elemItr != domain.end(); ++elemItr)
11482     {
11483       const SMDS_MeshElement* anElem = *elemItr;
11484       int idombisdeb = idom + 1 ;
11485       // check if the element belongs to a domain further in the list
11486       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11487       {
11488         const TIDSortedElemSet& domainbis = theElems[idombis];
11489         if ( domainbis.count( anElem ))
11490         {
11491           MESSAGE(".... Domain #" << idom);
11492           MESSAGE(".... Domain #" << idombis);
11493           throw SALOME_Exception("The domains are not disjoint.");
11494           return false ;
11495         }
11496       }
11497     }
11498   }
11499
11500   for (int idom = 0; idom < nbDomains; idom++)
11501   {
11502
11503     // --- build a map (face to duplicate --> volume to modify)
11504     //     with all the faces shared by 2 domains (group of elements)
11505     //     and corresponding volume of this domain, for each shared face.
11506     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11507
11508     //MESSAGE("... Neighbors of domain #" << idom);
11509     const TIDSortedElemSet& domain = theElems[idom];
11510     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11511     for (; elemItr != domain.end(); ++elemItr)
11512     {
11513       const SMDS_MeshElement* anElem = *elemItr;
11514       if (!anElem)
11515         continue;
11516       vtkIdType vtkId = anElem->GetVtkID();
11517       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11518       int neighborsVtkIds[NBMAXNEIGHBORS];
11519       int downIds[NBMAXNEIGHBORS];
11520       unsigned char downTypes[NBMAXNEIGHBORS];
11521       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11522       for (int n = 0; n < nbNeighbors; n++)
11523       {
11524         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11525         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11526         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11527         {
11528           bool ok = false;
11529           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11530           {
11531             // MESSAGE("Domain " << idombis);
11532             const TIDSortedElemSet& domainbis = theElems[idombis];
11533             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11534           }
11535           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11536           {
11537             DownIdType face(downIds[n], downTypes[n]);
11538             if (!faceDomains[face].count(idom))
11539             {
11540               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11541               celldom[vtkId] = idom;
11542               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11543             }
11544             if ( !ok )
11545             {
11546               theRestDomElems.insert( elem );
11547               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11548               celldom[neighborsVtkIds[n]] = iRestDom;
11549             }
11550           }
11551         }
11552       }
11553     }
11554   }
11555
11556   //MESSAGE("Number of shared faces " << faceDomains.size());
11557   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11558
11559   // --- explore the shared faces domain by domain,
11560   //     explore the nodes of the face and see if they belong to a cell in the domain,
11561   //     which has only a node or an edge on the border (not a shared face)
11562
11563   for (int idomain = idom0; idomain < nbDomains; idomain++)
11564   {
11565     //MESSAGE("Domain " << idomain);
11566     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11567     itface = faceDomains.begin();
11568     for (; itface != faceDomains.end(); ++itface)
11569     {
11570       const std::map<int, int>& domvol = itface->second;
11571       if (!domvol.count(idomain))
11572         continue;
11573       DownIdType face = itface->first;
11574       //MESSAGE(" --- face " << face.cellId);
11575       std::set<int> oldNodes;
11576       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11577       std::set<int>::iterator itn = oldNodes.begin();
11578       for (; itn != oldNodes.end(); ++itn)
11579       {
11580         int oldId = *itn;
11581         //MESSAGE("     node " << oldId);
11582         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11583         for (int i=0; i<l.ncells; i++)
11584         {
11585           int vtkId = l.cells[i];
11586           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11587           if (!domain.count(anElem))
11588             continue;
11589           int vtkType = grid->GetCellType(vtkId);
11590           int downId = grid->CellIdToDownId(vtkId);
11591           if (downId < 0)
11592           {
11593             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11594             continue; // not OK at this stage of the algorithm:
11595             //no cells created after BuildDownWardConnectivity
11596           }
11597           DownIdType aCell(downId, vtkType);
11598           cellDomains[aCell][idomain] = vtkId;
11599           celldom[vtkId] = idomain;
11600           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11601         }
11602       }
11603     }
11604   }
11605
11606   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11607   //     for each shared face, get the nodes
11608   //     for each node, for each domain of the face, create a clone of the node
11609
11610   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11611   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11612   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11613
11614   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11615   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11616   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11617
11618   //MESSAGE(".. Duplication of the nodes");
11619   for (int idomain = idom0; idomain < nbDomains; idomain++)
11620   {
11621     itface = faceDomains.begin();
11622     for (; itface != faceDomains.end(); ++itface)
11623     {
11624       const std::map<int, int>& domvol = itface->second;
11625       if (!domvol.count(idomain))
11626         continue;
11627       DownIdType face = itface->first;
11628       //MESSAGE(" --- face " << face.cellId);
11629       std::set<int> oldNodes;
11630       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11631       std::set<int>::iterator itn = oldNodes.begin();
11632       for (; itn != oldNodes.end(); ++itn)
11633       {
11634         int oldId = *itn;
11635         if (nodeDomains[oldId].empty())
11636         {
11637           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11638           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11639         }
11640         std::map<int, int>::const_iterator itdom = domvol.begin();
11641         for (; itdom != domvol.end(); ++itdom)
11642         {
11643           int idom = itdom->first;
11644           //MESSAGE("         domain " << idom);
11645           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11646           {
11647             if (nodeDomains[oldId].size() >= 2) // a multiple node
11648             {
11649               vector<int> orderedDoms;
11650               //MESSAGE("multiple node " << oldId);
11651               if (mutipleNodes.count(oldId))
11652                 orderedDoms = mutipleNodes[oldId];
11653               else
11654               {
11655                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11656                 for (; it != nodeDomains[oldId].end(); ++it)
11657                   orderedDoms.push_back(it->first);
11658               }
11659               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11660               //stringstream txt;
11661               //for (int i=0; i<orderedDoms.size(); i++)
11662               //  txt << orderedDoms[i] << " ";
11663               //MESSAGE("orderedDoms " << txt.str());
11664               mutipleNodes[oldId] = orderedDoms;
11665             }
11666             double *coords = grid->GetPoint(oldId);
11667             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11668             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11669             int newId = newNode->GetVtkID();
11670             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11671             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11672           }
11673         }
11674       }
11675     }
11676   }
11677
11678   //MESSAGE(".. Creation of elements");
11679   for (int idomain = idom0; idomain < nbDomains; idomain++)
11680   {
11681     itface = faceDomains.begin();
11682     for (; itface != faceDomains.end(); ++itface)
11683     {
11684       std::map<int, int> domvol = itface->second;
11685       if (!domvol.count(idomain))
11686         continue;
11687       DownIdType face = itface->first;
11688       //MESSAGE(" --- face " << face.cellId);
11689       std::set<int> oldNodes;
11690       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11691       int nbMultipleNodes = 0;
11692       std::set<int>::iterator itn = oldNodes.begin();
11693       for (; itn != oldNodes.end(); ++itn)
11694       {
11695         int oldId = *itn;
11696         if (mutipleNodes.count(oldId))
11697           nbMultipleNodes++;
11698       }
11699       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11700       {
11701         //MESSAGE("multiple Nodes detected on a shared face");
11702         int downId = itface->first.cellId;
11703         unsigned char cellType = itface->first.cellType;
11704         // --- shared edge or shared face ?
11705         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11706         {
11707           int nodes[3];
11708           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11709           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11710             if (mutipleNodes.count(nodes[i]))
11711               if (!mutipleNodesToFace.count(nodes[i]))
11712                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11713         }
11714         else // shared face (between two volumes)
11715         {
11716           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11717           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11718           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11719           for (int ie =0; ie < nbEdges; ie++)
11720           {
11721             int nodes[3];
11722             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11723             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11724             {
11725               vector<int> vn0 = mutipleNodes[nodes[0]];
11726               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11727               vector<int> doms;
11728               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11729                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11730                   if ( vn0[i0] == vn1[i1] )
11731                     doms.push_back( vn0[ i0 ]);
11732               if ( doms.size() > 2 )
11733               {
11734                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11735                 double *coords = grid->GetPoint(nodes[0]);
11736                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11737                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11738                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11739                 gp_Pnt gref;
11740                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11741                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11742                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11743                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11744                 for ( size_t id = 0; id < doms.size(); id++ )
11745                 {
11746                   int idom = doms[id];
11747                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11748                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11749                   {
11750                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11751                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11752                     if (domain.count(elem))
11753                     {
11754                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11755                       domvol[idom] = (SMDS_MeshVolume*) svol;
11756                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11757                       double values[3] = { 0,0,0 };
11758                       vtkIdType npts = 0;
11759                       vtkIdType const *pts(nullptr);
11760                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11761                       for ( vtkIdType i = 0; i < npts; ++i )
11762                       {
11763                         double *coords = grid->GetPoint( pts[i] );
11764                         for ( int j = 0; j < 3; ++j )
11765                           values[j] += coords[j] / npts;
11766                       }
11767                       if ( id == 0 )
11768                       {
11769                         gref.SetCoord( values[0], values[1], values[2] );
11770                         angleDom[idom] = 0;
11771                       }
11772                       else
11773                       {
11774                         gp_Pnt g( values[0], values[1], values[2] );
11775                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11776                         //MESSAGE("  angle=" << angleDom[idom]);
11777                       }
11778                       break;
11779                     }
11780                   }
11781                 }
11782                 map<double, int> sortedDom; // sort domains by angle
11783                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11784                   sortedDom[ia->second] = ia->first;
11785                 vector<int> vnodes;
11786                 vector<int> vdom;
11787                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11788                 {
11789                   vdom.push_back(ib->second);
11790                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11791                 }
11792                 for (int ino = 0; ino < nbNodes; ino++)
11793                   vnodes.push_back(nodes[ino]);
11794                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11795               }
11796             }
11797           }
11798         }
11799       }
11800     }
11801   }
11802
11803   // --- iterate on shared faces (volumes to modify, face to extrude)
11804   //     get node id's of the face (id SMDS = id VTK)
11805   //     create flat element with old and new nodes if requested
11806
11807   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11808   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11809
11810   std::map<int, std::map<long,int> > nodeQuadDomains;
11811   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11812
11813   //MESSAGE(".. Creation of elements: simple junction");
11814   if ( createJointElems )
11815   {
11816     string joints2DName = "joints2D";
11817     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11818     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11819     string joints3DName = "joints3D";
11820     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11821     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11822
11823     itface = faceDomains.begin();
11824     for (; itface != faceDomains.end(); ++itface)
11825     {
11826       DownIdType face = itface->first;
11827       std::set<int> oldNodes;
11828       std::set<int>::iterator itn;
11829       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11830
11831       std::map<int, int>          domvol = itface->second;
11832       std::map<int, int>::iterator itdom = domvol.begin();
11833       int     dom1 = itdom->first;
11834       int vtkVolId = itdom->second;
11835       itdom++;
11836       int           dom2 = itdom->first;
11837       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11838                                                        nodeQuadDomains);
11839       stringstream grpname;
11840       grpname << "j_";
11841       if (dom1 < dom2)
11842         grpname << dom1 << "_" << dom2;
11843       else
11844         grpname << dom2 << "_" << dom1;
11845       string namegrp = grpname.str();
11846       if (!mapOfJunctionGroups.count(namegrp))
11847         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11848       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11849       if (sgrp)
11850         sgrp->Add(vol->GetID());
11851       if (vol->GetType() == SMDSAbs_Volume)
11852         joints3DGrp->Add(vol->GetID());
11853       else if (vol->GetType() == SMDSAbs_Face)
11854         joints2DGrp->Add(vol->GetID());
11855     }
11856   }
11857
11858   // --- create volumes on multiple domain intersection if requested
11859   //     iterate on mutipleNodesToFace
11860   //     iterate on edgesMultiDomains
11861
11862   //MESSAGE(".. Creation of elements: multiple junction");
11863   if (createJointElems)
11864   {
11865     // --- iterate on mutipleNodesToFace
11866
11867     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11868     for (; itn != mutipleNodesToFace.end(); ++itn)
11869     {
11870       int node = itn->first;
11871       vector<int> orderDom = itn->second;
11872       vector<vtkIdType> orderedNodes;
11873       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11874         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11875       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11876
11877       stringstream grpname;
11878       grpname << "m2j_";
11879       grpname << 0 << "_" << 0;
11880       string namegrp = grpname.str();
11881       if (!mapOfJunctionGroups.count(namegrp))
11882         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11883       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11884       if (sgrp)
11885         sgrp->Add(face->GetID());
11886     }
11887
11888     // --- iterate on edgesMultiDomains
11889
11890     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11891     for (; ite != edgesMultiDomains.end(); ++ite)
11892     {
11893       vector<int>    nodes = ite->first;
11894       vector<int> orderDom = ite->second;
11895       vector<vtkIdType> orderedNodes;
11896       if (nodes.size() == 2)
11897       {
11898         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11899         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11900           if ( orderDom.size() == 3 )
11901             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11902               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11903           else
11904             for (int idom = orderDom.size()-1; idom >=0; idom--)
11905               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11906         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11907
11908         string namegrp = "jointsMultiples";
11909         if (!mapOfJunctionGroups.count(namegrp))
11910           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11911         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11912         if (sgrp)
11913           sgrp->Add(vol->GetID());
11914       }
11915       else
11916       {
11917         //INFOS("Quadratic multiple joints not implemented");
11918         // TODO quadratic nodes
11919       }
11920     }
11921   }
11922
11923   // --- list the explicit faces and edges of the mesh that need to be modified,
11924   //     i.e. faces and edges built with one or more duplicated nodes.
11925   //     associate these faces or edges to their corresponding domain.
11926   //     only the first domain found is kept when a face or edge is shared
11927
11928   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11929   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11930
11931   //MESSAGE(".. Modification of elements");
11932   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
11933   for (int idomain = idom0; idomain < nbDomains; idomain++)
11934   {
11935     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11936     for (; itnod != nodeDomains.end(); ++itnod)
11937     {
11938       int oldId = itnod->first;
11939       //MESSAGE("     node " << oldId);
11940       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11941       for (int i = 0; i < l.ncells; i++)
11942       {
11943         int vtkId = l.cells[i];
11944         int vtkType = grid->GetCellType(vtkId);
11945         int downId = grid->CellIdToDownId(vtkId);
11946         if (downId < 0)
11947           continue; // new cells: not to be modified
11948         DownIdType aCell(downId, vtkType);
11949         int volParents[1000];
11950         int nbvol = 0;
11951         nbvol = grid->GetParentVolumes(volParents, vtkId);
11952         if ( domainType == SMDSAbs_Volume )
11953         {
11954           nbvol = grid->GetParentVolumes(volParents, vtkId);
11955         }
11956         else // domainType == SMDSAbs_Face
11957         {
11958           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
11959           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
11960           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
11961           for (int i=0; i< nbFaces; i++)
11962           {
11963             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
11964             if (vtkFaceId >= 0)
11965               volParents[nbvol++] = vtkFaceId;
11966           }
11967         }
11968         for (int j = 0; j < nbvol; j++)
11969           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11970             if (!feDom.count(vtkId))
11971             {
11972               feDom[vtkId] = idomain;
11973               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11974               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11975               //        << " type " << vtkType << " downId " << downId);
11976             }
11977       }
11978     }
11979   }
11980
11981   // --- iterate on shared faces (volumes to modify, face to extrude)
11982   //     get node id's of the face
11983   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11984
11985   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11986   for (int m=0; m<3; m++)
11987   {
11988     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11989     itface = (*amap).begin();
11990     for (; itface != (*amap).end(); ++itface)
11991     {
11992       DownIdType face = itface->first;
11993       std::set<int> oldNodes;
11994       std::set<int>::iterator itn;
11995       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11996       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11997       std::map<int, int> localClonedNodeIds;
11998
11999       std::map<int, int> domvol = itface->second;
12000       std::map<int, int>::iterator itdom = domvol.begin();
12001       for (; itdom != domvol.end(); ++itdom)
12002       {
12003         int idom = itdom->first;
12004         int vtkVolId = itdom->second;
12005         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12006         localClonedNodeIds.clear();
12007         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12008         {
12009           int oldId = *itn;
12010           if (nodeDomains[oldId].count(idom))
12011           {
12012             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12013             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12014           }
12015         }
12016         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12017       }
12018     }
12019   }
12020
12021   // Remove empty groups (issue 0022812)
12022   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12023   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12024   {
12025     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12026       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12027   }
12028
12029   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12030   grid->DeleteLinks();
12031
12032   CHRONOSTOP(50);
12033   counters::stats();
12034   return true;
12035 }
12036
12037 /*!
12038  * \brief Double nodes on some external faces and create flat elements.
12039  * Flat elements are mainly used by some types of mechanic calculations.
12040  *
12041  * Each group of the list must be constituted of faces.
12042  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12043  * @param theElems - list of groups of faces, where a group of faces is a set of
12044  * SMDS_MeshElements sorted by Id.
12045  * @return TRUE if operation has been completed successfully, FALSE otherwise
12046  */
12047 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12048 {
12049   // MESSAGE("-------------------------------------------------");
12050   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12051   // MESSAGE("-------------------------------------------------");
12052
12053   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12054
12055   // --- For each group of faces
12056   //     duplicate the nodes, create a flat element based on the face
12057   //     replace the nodes of the faces by their clones
12058
12059   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12060   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12061   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12062
12063   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12064   {
12065     const TIDSortedElemSet&           domain = theElems[idom];
12066     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12067     for ( ; elemItr != domain.end(); ++elemItr )
12068     {
12069       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12070       if (!aFace)
12071         continue;
12072       // MESSAGE("aFace=" << aFace->GetID());
12073       bool isQuad = aFace->IsQuadratic();
12074       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12075
12076       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12077
12078       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12079       while (nodeIt->more())
12080       {
12081         const SMDS_MeshNode* node = nodeIt->next();
12082         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12083         if (isMedium)
12084           ln2.push_back(node);
12085         else
12086           ln0.push_back(node);
12087
12088         const SMDS_MeshNode* clone = 0;
12089         if (!clonedNodes.count(node))
12090         {
12091           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12092           copyPosition( node, clone );
12093           clonedNodes[node] = clone;
12094         }
12095         else
12096           clone = clonedNodes[node];
12097
12098         if (isMedium)
12099           ln3.push_back(clone);
12100         else
12101           ln1.push_back(clone);
12102
12103         const SMDS_MeshNode* inter = 0;
12104         if (isQuad && (!isMedium))
12105         {
12106           if (!intermediateNodes.count(node))
12107           {
12108             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12109             copyPosition( node, inter );
12110             intermediateNodes[node] = inter;
12111           }
12112           else
12113             inter = intermediateNodes[node];
12114           ln4.push_back(inter);
12115         }
12116       }
12117
12118       // --- extrude the face
12119
12120       vector<const SMDS_MeshNode*> ln;
12121       SMDS_MeshVolume* vol = 0;
12122       vtkIdType aType = aFace->GetVtkType();
12123       switch (aType)
12124       {
12125       case VTK_TRIANGLE:
12126         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12127         // MESSAGE("vol prism " << vol->GetID());
12128         ln.push_back(ln1[0]);
12129         ln.push_back(ln1[1]);
12130         ln.push_back(ln1[2]);
12131         break;
12132       case VTK_QUAD:
12133         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12134         // MESSAGE("vol hexa " << vol->GetID());
12135         ln.push_back(ln1[0]);
12136         ln.push_back(ln1[1]);
12137         ln.push_back(ln1[2]);
12138         ln.push_back(ln1[3]);
12139         break;
12140       case VTK_QUADRATIC_TRIANGLE:
12141         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12142                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12143         // MESSAGE("vol quad prism " << vol->GetID());
12144         ln.push_back(ln1[0]);
12145         ln.push_back(ln1[1]);
12146         ln.push_back(ln1[2]);
12147         ln.push_back(ln3[0]);
12148         ln.push_back(ln3[1]);
12149         ln.push_back(ln3[2]);
12150         break;
12151       case VTK_QUADRATIC_QUAD:
12152         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12153         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12154         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12155         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12156                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12157                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12158         // MESSAGE("vol quad hexa " << vol->GetID());
12159         ln.push_back(ln1[0]);
12160         ln.push_back(ln1[1]);
12161         ln.push_back(ln1[2]);
12162         ln.push_back(ln1[3]);
12163         ln.push_back(ln3[0]);
12164         ln.push_back(ln3[1]);
12165         ln.push_back(ln3[2]);
12166         ln.push_back(ln3[3]);
12167         break;
12168       case VTK_POLYGON:
12169         break;
12170       default:
12171         break;
12172       }
12173
12174       if (vol)
12175       {
12176         stringstream grpname;
12177         grpname << "jf_";
12178         grpname << idom;
12179         string namegrp = grpname.str();
12180         if (!mapOfJunctionGroups.count(namegrp))
12181           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12182         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12183         if (sgrp)
12184           sgrp->Add(vol->GetID());
12185       }
12186
12187       // --- modify the face
12188
12189       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12190     }
12191   }
12192   return true;
12193 }
12194
12195 /*!
12196  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12197  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12198  *  groups of faces to remove inside the object, (idem edges).
12199  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12200  */
12201 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12202                                       const TopoDS_Shape&             theShape,
12203                                       SMESH_NodeSearcher*             theNodeSearcher,
12204                                       const char*                     groupName,
12205                                       std::vector<double>&            nodesCoords,
12206                                       std::vector<std::vector<int> >& listOfListOfNodes)
12207 {
12208   // MESSAGE("--------------------------------");
12209   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12210   // MESSAGE("--------------------------------");
12211
12212   // --- zone of volumes to remove is given :
12213   //     1 either by a geom shape (one or more vertices) and a radius,
12214   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12215   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12216   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12217   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12218   //     defined by it's name.
12219
12220   SMESHDS_GroupBase* groupDS = 0;
12221   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12222   while ( groupIt->more() )
12223   {
12224     groupDS = 0;
12225     SMESH_Group * group = groupIt->next();
12226     if ( !group ) continue;
12227     groupDS = group->GetGroupDS();
12228     if ( !groupDS || groupDS->IsEmpty() ) continue;
12229     std::string grpName = group->GetName();
12230     //MESSAGE("grpName=" << grpName);
12231     if (grpName == groupName)
12232       break;
12233     else
12234       groupDS = 0;
12235   }
12236
12237   bool isNodeGroup = false;
12238   bool isNodeCoords = false;
12239   if (groupDS)
12240   {
12241     if (groupDS->GetType() != SMDSAbs_Node)
12242       return;
12243     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12244   }
12245
12246   if (nodesCoords.size() > 0)
12247     isNodeCoords = true; // a list o nodes given by their coordinates
12248   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12249
12250   // --- define groups to build
12251
12252   // --- group of SMDS volumes
12253   string grpvName = groupName;
12254   grpvName += "_vol";
12255   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12256   if (!grp)
12257   {
12258     MESSAGE("group not created " << grpvName);
12259     return;
12260   }
12261   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12262
12263   // --- group of SMDS faces on the skin
12264   string grpsName = groupName;
12265   grpsName += "_skin";
12266   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12267   if (!grps)
12268   {
12269     MESSAGE("group not created " << grpsName);
12270     return;
12271   }
12272   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12273
12274   // --- group of SMDS faces internal (several shapes)
12275   string grpiName = groupName;
12276   grpiName += "_internalFaces";
12277   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12278   if (!grpi)
12279   {
12280     MESSAGE("group not created " << grpiName);
12281     return;
12282   }
12283   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12284
12285   // --- group of SMDS faces internal (several shapes)
12286   string grpeiName = groupName;
12287   grpeiName += "_internalEdges";
12288   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12289   if (!grpei)
12290   {
12291     MESSAGE("group not created " << grpeiName);
12292     return;
12293   }
12294   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12295
12296   // --- build downward connectivity
12297
12298   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12299   meshDS->BuildDownWardConnectivity(true);
12300   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12301
12302   // --- set of volumes detected inside
12303
12304   std::set<int> setOfInsideVol;
12305   std::set<int> setOfVolToCheck;
12306
12307   std::vector<gp_Pnt> gpnts;
12308
12309   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12310   {
12311     //MESSAGE("group of nodes provided");
12312     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12313     while ( elemIt->more() )
12314     {
12315       const SMDS_MeshElement* elem = elemIt->next();
12316       if (!elem)
12317         continue;
12318       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12319       if (!node)
12320         continue;
12321       SMDS_MeshElement* vol = 0;
12322       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12323       while (volItr->more())
12324       {
12325         vol = (SMDS_MeshElement*)volItr->next();
12326         setOfInsideVol.insert(vol->GetVtkID());
12327         sgrp->Add(vol->GetID());
12328       }
12329     }
12330   }
12331   else if (isNodeCoords)
12332   {
12333     //MESSAGE("list of nodes coordinates provided");
12334     size_t i = 0;
12335     int k = 0;
12336     while ( i < nodesCoords.size()-2 )
12337     {
12338       double x = nodesCoords[i++];
12339       double y = nodesCoords[i++];
12340       double z = nodesCoords[i++];
12341       gp_Pnt p = gp_Pnt(x, y ,z);
12342       gpnts.push_back(p);
12343       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12344       k++;
12345     }
12346   }
12347   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12348   {
12349     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12350     TopTools_IndexedMapOfShape vertexMap;
12351     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12352     gp_Pnt p = gp_Pnt(0,0,0);
12353     if (vertexMap.Extent() < 1)
12354       return;
12355
12356     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12357     {
12358       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12359       p = BRep_Tool::Pnt(vertex);
12360       gpnts.push_back(p);
12361       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12362     }
12363   }
12364
12365   if (gpnts.size() > 0)
12366   {
12367     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12368     //MESSAGE("startNode->nodeId " << nodeId);
12369
12370     double radius2 = radius*radius;
12371     //MESSAGE("radius2 " << radius2);
12372
12373     // --- volumes on start node
12374
12375     setOfVolToCheck.clear();
12376     SMDS_MeshElement* startVol = 0;
12377     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12378     while (volItr->more())
12379     {
12380       startVol = (SMDS_MeshElement*)volItr->next();
12381       setOfVolToCheck.insert(startVol->GetVtkID());
12382     }
12383     if (setOfVolToCheck.empty())
12384     {
12385       MESSAGE("No volumes found");
12386       return;
12387     }
12388
12389     // --- starting with central volumes then their neighbors, check if they are inside
12390     //     or outside the domain, until no more new neighbor volume is inside.
12391     //     Fill the group of inside volumes
12392
12393     std::map<int, double> mapOfNodeDistance2;
12394     std::set<int> setOfOutsideVol;
12395     while (!setOfVolToCheck.empty())
12396     {
12397       std::set<int>::iterator it = setOfVolToCheck.begin();
12398       int vtkId = *it;
12399       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12400       bool volInside = false;
12401       vtkIdType npts = 0;
12402       vtkIdType const *pts(nullptr);
12403       grid->GetCellPoints(vtkId, npts, pts);
12404       for (int i=0; i<npts; i++)
12405       {
12406         double distance2 = 0;
12407         if (mapOfNodeDistance2.count(pts[i]))
12408         {
12409           distance2 = mapOfNodeDistance2[pts[i]];
12410           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12411         }
12412         else
12413         {
12414           double *coords = grid->GetPoint(pts[i]);
12415           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12416           distance2 = 1.E40;
12417           for ( size_t j = 0; j < gpnts.size(); j++ )
12418           {
12419             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12420             if (d2 < distance2)
12421             {
12422               distance2 = d2;
12423               if (distance2 < radius2)
12424                 break;
12425             }
12426           }
12427           mapOfNodeDistance2[pts[i]] = distance2;
12428           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12429         }
12430         if (distance2 < radius2)
12431         {
12432           volInside = true; // one or more nodes inside the domain
12433           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12434           break;
12435         }
12436       }
12437       if (volInside)
12438       {
12439         setOfInsideVol.insert(vtkId);
12440         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12441         int neighborsVtkIds[NBMAXNEIGHBORS];
12442         int downIds[NBMAXNEIGHBORS];
12443         unsigned char downTypes[NBMAXNEIGHBORS];
12444         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12445         for (int n = 0; n < nbNeighbors; n++)
12446           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12447             setOfVolToCheck.insert(neighborsVtkIds[n]);
12448       }
12449       else
12450       {
12451         setOfOutsideVol.insert(vtkId);
12452         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12453       }
12454       setOfVolToCheck.erase(vtkId);
12455     }
12456   }
12457
12458   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12459   //     If yes, add the volume to the inside set
12460
12461   bool addedInside = true;
12462   std::set<int> setOfVolToReCheck;
12463   while (addedInside)
12464   {
12465     //MESSAGE(" --------------------------- re check");
12466     addedInside = false;
12467     std::set<int>::iterator itv = setOfInsideVol.begin();
12468     for (; itv != setOfInsideVol.end(); ++itv)
12469     {
12470       int vtkId = *itv;
12471       int neighborsVtkIds[NBMAXNEIGHBORS];
12472       int downIds[NBMAXNEIGHBORS];
12473       unsigned char downTypes[NBMAXNEIGHBORS];
12474       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12475       for (int n = 0; n < nbNeighbors; n++)
12476         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12477           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12478     }
12479     setOfVolToCheck = setOfVolToReCheck;
12480     setOfVolToReCheck.clear();
12481     while  (!setOfVolToCheck.empty())
12482     {
12483       std::set<int>::iterator it = setOfVolToCheck.begin();
12484       int vtkId = *it;
12485       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12486       {
12487         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12488         int countInside = 0;
12489         int neighborsVtkIds[NBMAXNEIGHBORS];
12490         int downIds[NBMAXNEIGHBORS];
12491         unsigned char downTypes[NBMAXNEIGHBORS];
12492         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12493         for (int n = 0; n < nbNeighbors; n++)
12494           if (setOfInsideVol.count(neighborsVtkIds[n]))
12495             countInside++;
12496         //MESSAGE("countInside " << countInside);
12497         if (countInside > 1)
12498         {
12499           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12500           setOfInsideVol.insert(vtkId);
12501           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12502           addedInside = true;
12503         }
12504         else
12505           setOfVolToReCheck.insert(vtkId);
12506       }
12507       setOfVolToCheck.erase(vtkId);
12508     }
12509   }
12510
12511   // --- map of Downward faces at the boundary, inside the global volume
12512   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12513   //     fill group of SMDS faces inside the volume (when several volume shapes)
12514   //     fill group of SMDS faces on the skin of the global volume (if skin)
12515
12516   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12517   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12518   std::set<int>::iterator it = setOfInsideVol.begin();
12519   for (; it != setOfInsideVol.end(); ++it)
12520   {
12521     int vtkId = *it;
12522     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12523     int neighborsVtkIds[NBMAXNEIGHBORS];
12524     int downIds[NBMAXNEIGHBORS];
12525     unsigned char downTypes[NBMAXNEIGHBORS];
12526     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12527     for (int n = 0; n < nbNeighbors; n++)
12528     {
12529       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12530       if (neighborDim == 3)
12531       {
12532         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12533         {
12534           DownIdType face(downIds[n], downTypes[n]);
12535           boundaryFaces[face] = vtkId;
12536         }
12537         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12538         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12539         if (vtkFaceId >= 0)
12540         {
12541           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12542           // find also the smds edges on this face
12543           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12544           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12545           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12546           for (int i = 0; i < nbEdges; i++)
12547           {
12548             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12549             if (vtkEdgeId >= 0)
12550               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12551           }
12552         }
12553       }
12554       else if (neighborDim == 2) // skin of the volume
12555       {
12556         DownIdType face(downIds[n], downTypes[n]);
12557         skinFaces[face] = vtkId;
12558         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12559         if (vtkFaceId >= 0)
12560           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12561       }
12562     }
12563   }
12564
12565   // --- identify the edges constituting the wire of each subshape on the skin
12566   //     define polylines with the nodes of edges, equivalent to wires
12567   //     project polylines on subshapes, and partition, to get geom faces
12568
12569   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12570   std::set<int>                 shapeIds;
12571
12572   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12573   while (itelem->more())
12574   {
12575     const SMDS_MeshElement *elem = itelem->next();
12576     int shapeId = elem->getshapeId();
12577     int   vtkId = elem->GetVtkID();
12578     if (!shapeIdToVtkIdSet.count(shapeId))
12579     {
12580       shapeIds.insert(shapeId);
12581     }
12582     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12583   }
12584
12585   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12586   std::set<DownIdType, DownIdCompare> emptyEdges;
12587
12588   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12589   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12590   {
12591     int shapeId = itShape->first;
12592     //MESSAGE(" --- Shape ID --- "<< shapeId);
12593     shapeIdToEdges[shapeId] = emptyEdges;
12594
12595     std::vector<int> nodesEdges;
12596
12597     std::set<int>::iterator its = itShape->second.begin();
12598     for (; its != itShape->second.end(); ++its)
12599     {
12600       int vtkId = *its;
12601       //MESSAGE("     " << vtkId);
12602       int neighborsVtkIds[NBMAXNEIGHBORS];
12603       int downIds[NBMAXNEIGHBORS];
12604       unsigned char downTypes[NBMAXNEIGHBORS];
12605       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12606       for (int n = 0; n < nbNeighbors; n++)
12607       {
12608         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12609           continue;
12610         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12611         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12612         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12613         {
12614           DownIdType edge(downIds[n], downTypes[n]);
12615           if (!shapeIdToEdges[shapeId].count(edge))
12616           {
12617             shapeIdToEdges[shapeId].insert(edge);
12618             int vtkNodeId[3];
12619             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12620             nodesEdges.push_back(vtkNodeId[0]);
12621             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12622             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12623           }
12624         }
12625       }
12626     }
12627
12628     std::list<int> order;
12629     if (nodesEdges.size() > 0)
12630     {
12631       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12632       nodesEdges[0] = -1;
12633       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12634       nodesEdges[1] = -1; // do not reuse this edge
12635       bool found = true;
12636       while (found)
12637       {
12638         int nodeTofind = order.back(); // try first to push back
12639         int i = 0;
12640         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12641           if (nodesEdges[i] == nodeTofind)
12642             break;
12643         if ( i == (int) nodesEdges.size() )
12644           found = false; // no follower found on back
12645         else
12646         {
12647           if (i%2) // odd ==> use the previous one
12648             if (nodesEdges[i-1] < 0)
12649               found = false;
12650             else
12651             {
12652               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12653               nodesEdges[i-1] = -1;
12654             }
12655           else // even ==> use the next one
12656             if (nodesEdges[i+1] < 0)
12657               found = false;
12658             else
12659             {
12660               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12661               nodesEdges[i+1] = -1;
12662             }
12663         }
12664         if (found)
12665           continue;
12666         // try to push front
12667         found = true;
12668         nodeTofind = order.front(); // try to push front
12669         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12670           if ( nodesEdges[i] == nodeTofind )
12671             break;
12672         if ( i == (int)nodesEdges.size() )
12673         {
12674           found = false; // no predecessor found on front
12675           continue;
12676         }
12677         if (i%2) // odd ==> use the previous one
12678           if (nodesEdges[i-1] < 0)
12679             found = false;
12680           else
12681           {
12682             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12683             nodesEdges[i-1] = -1;
12684           }
12685         else // even ==> use the next one
12686           if (nodesEdges[i+1] < 0)
12687             found = false;
12688           else
12689           {
12690             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12691             nodesEdges[i+1] = -1;
12692           }
12693       }
12694     }
12695
12696
12697     std::vector<int> nodes;
12698     nodes.push_back(shapeId);
12699     std::list<int>::iterator itl = order.begin();
12700     for (; itl != order.end(); itl++)
12701     {
12702       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12703       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12704     }
12705     listOfListOfNodes.push_back(nodes);
12706   }
12707
12708   //     partition geom faces with blocFissure
12709   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12710   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12711
12712   return;
12713 }
12714
12715
12716 //================================================================================
12717 /*!
12718  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12719  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12720  * \return TRUE if operation has been completed successfully, FALSE otherwise
12721  */
12722 //================================================================================
12723
12724 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12725 {
12726   // iterates on volume elements and detect all free faces on them
12727   SMESHDS_Mesh* aMesh = GetMeshDS();
12728   if (!aMesh)
12729     return false;
12730
12731   ElemFeatures faceType( SMDSAbs_Face );
12732   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12733   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12734   while(vIt->more())
12735   {
12736     const SMDS_MeshVolume* volume = vIt->next();
12737     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12738     vTool.SetExternalNormal();
12739     const int iQuad = volume->IsQuadratic();
12740     faceType.SetQuad( iQuad );
12741     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12742     {
12743       if (!vTool.IsFreeFace(iface))
12744         continue;
12745       nbFree++;
12746       vector<const SMDS_MeshNode *> nodes;
12747       int nbFaceNodes = vTool.NbFaceNodes(iface);
12748       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12749       int inode = 0;
12750       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12751         nodes.push_back(faceNodes[inode]);
12752
12753       if (iQuad) // add medium nodes
12754       {
12755         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12756           nodes.push_back(faceNodes[inode]);
12757         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12758           nodes.push_back(faceNodes[8]);
12759       }
12760       // add new face based on volume nodes
12761       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12762       {
12763         nbExisted++; // face already exists
12764       }
12765       else
12766       {
12767         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12768         nbCreated++;
12769       }
12770     }
12771   }
12772   return ( nbFree == ( nbExisted + nbCreated ));
12773 }
12774
12775 namespace
12776 {
12777   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12778   {
12779     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12780       return n;
12781     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12782   }
12783 }
12784 //================================================================================
12785 /*!
12786  * \brief Creates missing boundary elements
12787  *  \param elements - elements whose boundary is to be checked
12788  *  \param dimension - defines type of boundary elements to create
12789  *  \param group - a group to store created boundary elements in
12790  *  \param targetMesh - a mesh to store created boundary elements in
12791  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12792  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12793  *                                boundary elements will be copied into the targetMesh
12794  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12795  *                                boundary elements will be added into the new group
12796  *  \param aroundElements - if true, elements will be created on boundary of given
12797  *                          elements else, on boundary of the whole mesh.
12798  * \return nb of added boundary elements
12799  */
12800 //================================================================================
12801
12802 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12803                                        Bnd_Dimension           dimension,
12804                                        SMESH_Group*            group/*=0*/,
12805                                        SMESH_Mesh*             targetMesh/*=0*/,
12806                                        bool                    toCopyElements/*=false*/,
12807                                        bool                    toCopyExistingBoundary/*=false*/,
12808                                        bool                    toAddExistingBondary/*= false*/,
12809                                        bool                    aroundElements/*= false*/)
12810 {
12811   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12812   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12813   // hope that all elements are of the same type, do not check them all
12814   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12815     throw SALOME_Exception(LOCALIZED("wrong element type"));
12816
12817   if ( !targetMesh )
12818     toCopyElements = toCopyExistingBoundary = false;
12819
12820   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12821   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12822   int nbAddedBnd = 0;
12823
12824   // editor adding present bnd elements and optionally holding elements to add to the group
12825   SMESH_MeshEditor* presentEditor;
12826   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12827   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12828
12829   SMESH_MesherHelper helper( *myMesh );
12830   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12831   SMDS_VolumeTool vTool;
12832   TIDSortedElemSet avoidSet;
12833   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12834   size_t inode;
12835
12836   typedef vector<const SMDS_MeshNode*> TConnectivity;
12837   TConnectivity tgtNodes;
12838   ElemFeatures elemKind( missType ), elemToCopy;
12839
12840   vector<const SMDS_MeshElement*> presentBndElems;
12841   vector<TConnectivity>           missingBndElems;
12842   vector<int>                     freeFacets;
12843   TConnectivity nodes, elemNodes;
12844
12845   SMDS_ElemIteratorPtr eIt;
12846   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12847   else                  eIt = SMESHUtils::elemSetIterator( elements );
12848
12849   while ( eIt->more() )
12850   {
12851     const SMDS_MeshElement* elem = eIt->next();
12852     const int              iQuad = elem->IsQuadratic();
12853     elemKind.SetQuad( iQuad );
12854
12855     // ------------------------------------------------------------------------------------
12856     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12857     // ------------------------------------------------------------------------------------
12858     presentBndElems.clear();
12859     missingBndElems.clear();
12860     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12861     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12862     {
12863       const SMDS_MeshElement* otherVol = 0;
12864       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12865       {
12866         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12867              ( !aroundElements || elements.count( otherVol )))
12868           continue;
12869         freeFacets.push_back( iface );
12870       }
12871       if ( missType == SMDSAbs_Face )
12872         vTool.SetExternalNormal();
12873       for ( size_t i = 0; i < freeFacets.size(); ++i )
12874       {
12875         int                iface = freeFacets[i];
12876         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12877         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12878         if ( missType == SMDSAbs_Edge ) // boundary edges
12879         {
12880           nodes.resize( 2+iQuad );
12881           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12882           {
12883             for ( size_t j = 0; j < nodes.size(); ++j )
12884               nodes[ j ] = nn[ i+j ];
12885             if ( const SMDS_MeshElement* edge =
12886                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12887               presentBndElems.push_back( edge );
12888             else
12889               missingBndElems.push_back( nodes );
12890           }
12891         }
12892         else // boundary face
12893         {
12894           nodes.clear();
12895           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12896             nodes.push_back( nn[inode] ); // add corner nodes
12897           if (iQuad)
12898             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12899               nodes.push_back( nn[inode] ); // add medium nodes
12900           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12901           if ( iCenter > 0 )
12902             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12903
12904           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12905                                                                SMDSAbs_Face, /*noMedium=*/false ))
12906             presentBndElems.push_back( f );
12907           else
12908             missingBndElems.push_back( nodes );
12909
12910           if ( targetMesh != myMesh )
12911           {
12912             // add 1D elements on face boundary to be added to a new mesh
12913             const SMDS_MeshElement* edge;
12914             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12915             {
12916               if ( iQuad )
12917                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12918               else
12919                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12920               if ( edge && avoidSet.insert( edge ).second )
12921                 presentBndElems.push_back( edge );
12922             }
12923           }
12924         }
12925       }
12926     }
12927     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12928     {
12929       avoidSet.clear(), avoidSet.insert( elem );
12930       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12931                         SMDS_MeshElement::iterator() );
12932       elemNodes.push_back( elemNodes[0] );
12933       nodes.resize( 2 + iQuad );
12934       const int nbLinks = elem->NbCornerNodes();
12935       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12936       {
12937         nodes[0] = elemNodes[iN];
12938         nodes[1] = elemNodes[iN+1+iQuad];
12939         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12940           continue; // not free link
12941
12942         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12943         if ( const SMDS_MeshElement* edge =
12944              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12945           presentBndElems.push_back( edge );
12946         else
12947           missingBndElems.push_back( nodes );
12948       }
12949     }
12950
12951     // ---------------------------------
12952     // 2. Add missing boundary elements
12953     // ---------------------------------
12954     if ( targetMesh != myMesh )
12955       // instead of making a map of nodes in this mesh and targetMesh,
12956       // we create nodes with same IDs.
12957       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12958       {
12959         TConnectivity& srcNodes = missingBndElems[i];
12960         tgtNodes.resize( srcNodes.size() );
12961         for ( inode = 0; inode < srcNodes.size(); ++inode )
12962           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12963         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12964                                                                        missType,
12965                                                                        /*noMedium=*/false))
12966           continue;
12967         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12968         ++nbAddedBnd;
12969       }
12970     else
12971       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12972       {
12973         TConnectivity& nodes = missingBndElems[ i ];
12974         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12975                                                                        missType,
12976                                                                        /*noMedium=*/false))
12977           continue;
12978         SMDS_MeshElement* newElem =
12979           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12980         nbAddedBnd += bool( newElem );
12981
12982         // try to set a new element to a shape
12983         if ( myMesh->HasShapeToMesh() )
12984         {
12985           bool ok = true;
12986           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12987           const size_t nbN = nodes.size() / (iQuad+1 );
12988           for ( inode = 0; inode < nbN && ok; ++inode )
12989           {
12990             pair<int, TopAbs_ShapeEnum> i_stype =
12991               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12992             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12993               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12994           }
12995           if ( ok && mediumShapes.size() > 1 )
12996           {
12997             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12998             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12999             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13000             {
13001               if (( ok = ( stype_i->first != stype_i_0.first )))
13002                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13003                                         aMesh->IndexToShape( stype_i_0.second ));
13004             }
13005           }
13006           if ( ok && mediumShapes.begin()->first == missShapeType )
13007             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13008         }
13009       }
13010
13011     // ----------------------------------
13012     // 3. Copy present boundary elements
13013     // ----------------------------------
13014     if ( toCopyExistingBoundary )
13015       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13016       {
13017         const SMDS_MeshElement* e = presentBndElems[i];
13018         tgtNodes.resize( e->NbNodes() );
13019         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13020           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13021         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13022       }
13023     else // store present elements to add them to a group
13024       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13025       {
13026         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13027       }
13028
13029   } // loop on given elements
13030
13031   // ---------------------------------------------
13032   // 4. Fill group with boundary elements
13033   // ---------------------------------------------
13034   if ( group )
13035   {
13036     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13037       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13038         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13039   }
13040   tgtEditor.myLastCreatedElems.clear();
13041   tgtEditor2.myLastCreatedElems.clear();
13042
13043   // -----------------------
13044   // 5. Copy given elements
13045   // -----------------------
13046   if ( toCopyElements && targetMesh != myMesh )
13047   {
13048     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13049     else                  eIt = SMESHUtils::elemSetIterator( elements );
13050     while (eIt->more())
13051     {
13052       const SMDS_MeshElement* elem = eIt->next();
13053       tgtNodes.resize( elem->NbNodes() );
13054       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13055         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13056       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13057
13058       tgtEditor.myLastCreatedElems.clear();
13059     }
13060   }
13061   return nbAddedBnd;
13062 }
13063
13064 //================================================================================
13065 /*!
13066  * \brief Copy node position and set \a to node on the same geometry
13067  */
13068 //================================================================================
13069
13070 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13071                                      const SMDS_MeshNode* to )
13072 {
13073   if ( !from || !to ) return;
13074
13075   SMDS_PositionPtr pos = from->GetPosition();
13076   if ( !pos || from->getshapeId() < 1 ) return;
13077
13078   switch ( pos->GetTypeOfPosition() )
13079   {
13080   case SMDS_TOP_3DSPACE: break;
13081
13082   case SMDS_TOP_FACE:
13083   {
13084     SMDS_FacePositionPtr fPos = pos;
13085     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13086                                 fPos->GetUParameter(), fPos->GetVParameter() );
13087     break;
13088   }
13089   case SMDS_TOP_EDGE:
13090   {
13091     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13092     SMDS_EdgePositionPtr ePos = pos;
13093     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13094     break;
13095   }
13096   case SMDS_TOP_VERTEX:
13097   {
13098     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13099     break;
13100   }
13101   case SMDS_TOP_UNSPEC:
13102   default:;
13103   }
13104 }