Salome HOME
Update of CheckDone
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <ShapeAnalysis.hxx>
66 #include <ShapeAnalysis_Curve.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98 #include <boost/container/flat_set.hpp>
99
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
102
103 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104
105 #include <smIdType.hxx>
106
107 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108
109 using namespace std;
110 using namespace SMESH::Controls;
111
112 //=======================================================================
113 //function : SMESH_MeshEditor
114 //purpose  :
115 //=======================================================================
116
117 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
118   :myMesh( theMesh ) // theMesh may be NULL
119 {
120 }
121
122 //================================================================================
123 /*!
124  * \brief Return mesh DS
125  */
126 //================================================================================
127
128 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
129 {
130   return myMesh->GetMeshDS();
131 }
132
133
134 //================================================================================
135 /*!
136  * \brief Clears myLastCreatedNodes and myLastCreatedElems
137  */
138 //================================================================================
139
140 void SMESH_MeshEditor::ClearLastCreated()
141 {
142   SMESHUtils::FreeVector( myLastCreatedElems );
143   SMESHUtils::FreeVector( myLastCreatedNodes );
144 }
145
146 //================================================================================
147 /*!
148  * \brief Initializes members by an existing element
149  *  \param [in] elem - the source element
150  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
151  */
152 //================================================================================
153
154 SMESH_MeshEditor::ElemFeatures&
155 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
156 {
157   if ( elem )
158   {
159     myType = elem->GetType();
160     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
161     {
162       myIsPoly = elem->IsPoly();
163       if ( myIsPoly )
164       {
165         myIsQuad = elem->IsQuadratic();
166         if ( myType == SMDSAbs_Volume && !basicOnly )
167         {
168           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
169         }
170       }
171     }
172     else if ( myType == SMDSAbs_Ball && !basicOnly )
173     {
174       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175     }
176   }
177   return *this;
178 }
179
180 //=======================================================================
181 /*!
182  * \brief Add element
183  */
184 //=======================================================================
185
186 SMDS_MeshElement*
187 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
188                              const ElemFeatures&                  features)
189 {
190   SMDS_MeshElement* e = 0;
191   int nbnode = node.size();
192   SMESHDS_Mesh* mesh = GetMeshDS();
193   const smIdType ID = features.myID;
194
195   switch ( features.myType ) {
196   case SMDSAbs_Face:
197     if ( !features.myIsPoly ) {
198       if      (nbnode == 3) {
199         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
200         else           e = mesh->AddFace      (node[0], node[1], node[2] );
201       }
202       else if (nbnode == 4) {
203         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
204         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
205       }
206       else if (nbnode == 6) {
207         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
208                                                node[4], node[5], ID);
209         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
210                                                node[4], node[5] );
211       }
212       else if (nbnode == 7) {
213         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6], ID);
215         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
216                                                node[4], node[5], node[6] );
217       }
218       else if (nbnode == 8) {
219         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7], ID);
221         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], node[7] );
223       }
224       else if (nbnode == 9) {
225         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8], ID);
227         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
228                                                node[4], node[5], node[6], node[7], node[8] );
229       }
230     }
231     else if ( !features.myIsQuad )
232     {
233       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
234       else           e = mesh->AddPolygonalFace      (node    );
235     }
236     else if ( nbnode % 2 == 0 ) // just a protection
237     {
238       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
239       else           e = mesh->AddQuadPolygonalFace      (node    );
240     }
241     break;
242
243   case SMDSAbs_Volume:
244     if ( !features.myIsPoly ) {
245       if      (nbnode == 4) {
246         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
247         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
248       }
249       else if (nbnode == 5) {
250         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251                                                  node[4], ID);
252         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
253                                                  node[4] );
254       }
255       else if (nbnode == 6) {
256         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
257                                                  node[4], node[5], ID);
258         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
259                                                  node[4], node[5] );
260       }
261       else if (nbnode == 8) {
262         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7], ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7] );
266       }
267       else if (nbnode == 10) {
268         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9], ID);
271         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9] );
274       }
275       else if (nbnode == 12) {
276         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9], node[10], node[11], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10], node[11] );
282       }
283       else if (nbnode == 13) {
284         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10],node[11],
287                                                  node[12],ID);
288         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
289                                                  node[4], node[5], node[6], node[7],
290                                                  node[8], node[9], node[10],node[11],
291                                                  node[12] );
292       }
293       else if (nbnode == 15) {
294         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],node[13],node[14],ID);
298         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
299                                                  node[4], node[5], node[6], node[7],
300                                                  node[8], node[9], node[10],node[11],
301                                                  node[12],node[13],node[14] );
302       }
303       else if (nbnode == 18) {
304         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
305                                                  node[4], node[5], node[6], node[7],
306                                                  node[8], node[9], node[10],node[11],
307                                                  node[12],node[13],node[14],
308                                                  node[15],node[16],node[17],ID );
309         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
310                                                  node[4], node[5], node[6], node[7],
311                                                  node[8], node[9], node[10],node[11],
312                                                  node[12],node[13],node[14],
313                                                  node[15],node[16],node[17] );
314       }
315       else if (nbnode == 20) {
316         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
317                                                  node[4], node[5], node[6], node[7],
318                                                  node[8], node[9], node[10],node[11],
319                                                  node[12],node[13],node[14],node[15],
320                                                  node[16],node[17],node[18],node[19],ID);
321         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
322                                                  node[4], node[5], node[6], node[7],
323                                                  node[8], node[9], node[10],node[11],
324                                                  node[12],node[13],node[14],node[15],
325                                                  node[16],node[17],node[18],node[19] );
326       }
327       else if (nbnode == 27) {
328         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
329                                                  node[4], node[5], node[6], node[7],
330                                                  node[8], node[9], node[10],node[11],
331                                                  node[12],node[13],node[14],node[15],
332                                                  node[16],node[17],node[18],node[19],
333                                                  node[20],node[21],node[22],node[23],
334                                                  node[24],node[25],node[26], ID);
335         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
336                                                  node[4], node[5], node[6], node[7],
337                                                  node[8], node[9], node[10],node[11],
338                                                  node[12],node[13],node[14],node[15],
339                                                  node[16],node[17],node[18],node[19],
340                                                  node[20],node[21],node[22],node[23],
341                                                  node[24],node[25],node[26] );
342       }
343     }
344     else if ( !features.myIsQuad )
345     {
346       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
347       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
348     }
349     else
350     {
351       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
352       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
353     }
354     break;
355
356   case SMDSAbs_Edge:
357     if ( nbnode == 2 ) {
358       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
359       else           e = mesh->AddEdge      (node[0], node[1] );
360     }
361     else if ( nbnode == 3 ) {
362       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
363       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
364     }
365     break;
366
367   case SMDSAbs_0DElement:
368     if ( nbnode == 1 ) {
369       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
370       else           e = mesh->Add0DElement      (node[0] );
371     }
372     break;
373
374   case SMDSAbs_Node:
375     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
376     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
377     break;
378
379   case SMDSAbs_Ball:
380     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
381     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
382     break;
383
384   default:;
385   }
386   if ( e ) myLastCreatedElems.push_back( e );
387   return e;
388 }
389
390 //=======================================================================
391 /*!
392  * \brief Add element
393  */
394 //=======================================================================
395
396 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
397                                                const ElemFeatures&      features)
398 {
399   vector<const SMDS_MeshNode*> nodes;
400   nodes.reserve( nodeIDs.size() );
401   vector<smIdType>::const_iterator id = nodeIDs.begin();
402   while ( id != nodeIDs.end() ) {
403     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
404       nodes.push_back( node );
405     else
406       return 0;
407   }
408   return AddElement( nodes, features );
409 }
410
411 //=======================================================================
412 //function : Remove
413 //purpose  : Remove a node or an element.
414 //           Modify a compute state of sub-meshes which become empty
415 //=======================================================================
416
417 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
418                                    const bool                   isNodes )
419 {
420   ClearLastCreated();
421
422   SMESHDS_Mesh* aMesh = GetMeshDS();
423   set< SMESH_subMesh *> smmap;
424
425   smIdType removed = 0;
426   list<smIdType>::const_iterator it = theIDs.begin();
427   for ( ; it != theIDs.end(); it++ ) {
428     const SMDS_MeshElement * elem;
429     if ( isNodes )
430       elem = aMesh->FindNode( *it );
431     else
432       elem = aMesh->FindElement( *it );
433     if ( !elem )
434       continue;
435
436     // Notify VERTEX sub-meshes about modification
437     if ( isNodes ) {
438       const SMDS_MeshNode* node = cast2Node( elem );
439       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
440         if ( int aShapeID = node->getshapeId() )
441           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442             smmap.insert( sm );
443     }
444     // Find sub-meshes to notify about modification
445     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
446     //     while ( nodeIt->more() ) {
447     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
448     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
449     //       if ( aPosition.get() ) {
450     //         if ( int aShapeID = aPosition->GetShapeId() ) {
451     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
452     //             smmap.insert( sm );
453     //         }
454     //       }
455     //     }
456
457     // Do remove
458     if ( isNodes )
459       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
460     else
461       aMesh->RemoveElement( elem );
462     removed++;
463   }
464
465   // Notify sub-meshes about modification
466   if ( !smmap.empty() ) {
467     set< SMESH_subMesh *>::iterator smIt;
468     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
469       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470   }
471
472   //   // Check if the whole mesh becomes empty
473   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
474   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475
476   return removed;
477 }
478
479 //================================================================================
480 /*!
481  * \brief Remove a node and fill a hole appeared, by changing surrounding faces
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
486 {
487   if ( ! node )
488     return;
489
490   if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
491     throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
492
493   // check that only triangles surround the node
494   for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
495   {
496     const SMDS_MeshElement* face = fIt->next();
497     if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
498       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
499     if ( face->IsQuadratic() )
500       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
501   }
502
503   std::vector< const SMDS_MeshNode*> neighbours(2);
504   SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
505
506   bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
507
508   // if ( neighbours.size() == 2 ) // on boundary
509   // {
510   //   // check if theNode and neighbours are on a line
511   //   gp_Pnt pN = SMESH_NodeXYZ( node );
512   //   gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
513   //   gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
514   //   double dist01 = p0.Distance( p1 );
515   //   double    tol = 0.01 * dist01;
516   //   double  distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
517   //   bool   onLine = distN < tol;
518   //   toRemove = !onLine;
519   // }
520
521   if ( neighbours.empty() ) // not on boundary
522   {
523     TIDSortedElemSet linkedNodes;
524     GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
525     for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
526     if ( neighbours.empty() )
527       toRemove = true;
528   }
529
530   if ( toRemove )
531   {
532     this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
533     return;
534   }
535
536   // choose a node to replace by
537   const SMDS_MeshNode* nToReplace = nullptr;
538   SMESH_NodeXYZ           nodeXYZ = node;
539   double                  minDist = Precision::Infinite();
540   for ( const SMDS_MeshNode* n : neighbours )
541   {
542     double dist = nodeXYZ.SquareDistance( n );
543     if ( dist < minDist )
544     {
545       minDist = dist;
546       nToReplace = n;
547     }
548   }
549
550   // remove node + replace by nToReplace
551   std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
552   TListOfListOfNodes nodesToMerge( 1, nodeGroup );
553   this->MergeNodes( nodesToMerge );
554 }
555
556 //================================================================================
557 /*!
558  * \brief Create 0D elements on all nodes of the given object.
559  *  \param elements - Elements on whose nodes to create 0D elements; if empty,
560  *                    the all mesh is treated
561  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
562  *  \param duplicateElements - to add one more 0D element to a node or not
563  */
564 //================================================================================
565
566 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
567                                                    TIDSortedElemSet&       all0DElems,
568                                                    const bool              duplicateElements )
569 {
570   SMDS_ElemIteratorPtr elemIt;
571   if ( elements.empty() )
572   {
573     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
574   }
575   else
576   {
577     elemIt = SMESHUtils::elemSetIterator( elements );
578   }
579
580   while ( elemIt->more() )
581   {
582     const SMDS_MeshElement* e = elemIt->next();
583     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
584     while ( nodeIt->more() )
585     {
586       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
587       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
588       if ( duplicateElements || !it0D->more() )
589       {
590         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
591         all0DElems.insert( myLastCreatedElems.back() );
592       }
593       while ( it0D->more() )
594         all0DElems.insert( it0D->next() );
595     }
596   }
597 }
598
599 //=======================================================================
600 //function : FindShape
601 //purpose  : Return an index of the shape theElem is on
602 //           or zero if a shape not found
603 //=======================================================================
604
605 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
606 {
607   ClearLastCreated();
608
609   SMESHDS_Mesh * aMesh = GetMeshDS();
610   if ( aMesh->ShapeToMesh().IsNull() )
611     return 0;
612
613   int aShapeID = theElem->getshapeId();
614   if ( aShapeID < 1 )
615     return 0;
616
617   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
618     if ( sm->Contains( theElem ))
619       return aShapeID;
620
621   if ( theElem->GetType() == SMDSAbs_Node ) {
622     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
623   }
624   else {
625     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
626   }
627
628   TopoDS_Shape aShape; // the shape a node of theElem is on
629   if ( theElem->GetType() != SMDSAbs_Node )
630   {
631     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
632     while ( nodeIt->more() ) {
633       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
634       if ((aShapeID = node->getshapeId()) > 0) {
635         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
636           if ( sm->Contains( theElem ))
637             return aShapeID;
638           if ( aShape.IsNull() )
639             aShape = aMesh->IndexToShape( aShapeID );
640         }
641       }
642     }
643   }
644
645   // None of nodes is on a proper shape,
646   // find the shape among ancestors of aShape on which a node is
647   if ( !aShape.IsNull() ) {
648     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
649     for ( ; ancIt.More(); ancIt.Next() ) {
650       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
651       if ( sm && sm->Contains( theElem ))
652         return aMesh->ShapeToIndex( ancIt.Value() );
653     }
654   }
655   else
656   {
657     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
658     while ( const SMESHDS_SubMesh* sm = smIt->next() )
659       if ( sm->Contains( theElem ))
660         return sm->GetID();
661   }
662
663   return 0;
664 }
665
666 //=======================================================================
667 //function : IsMedium
668 //purpose  :
669 //=======================================================================
670
671 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
672                                 const SMDSAbs_ElementType typeToCheck)
673 {
674   bool isMedium = false;
675   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
676   while (it->more() && !isMedium ) {
677     const SMDS_MeshElement* elem = it->next();
678     isMedium = elem->IsMediumNode(node);
679   }
680   return isMedium;
681 }
682
683 //=======================================================================
684 //function : shiftNodesQuadTria
685 //purpose  : Shift nodes in the array corresponded to quadratic triangle
686 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
687 //=======================================================================
688
689 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
690 {
691   const SMDS_MeshNode* nd1 = aNodes[0];
692   aNodes[0] = aNodes[1];
693   aNodes[1] = aNodes[2];
694   aNodes[2] = nd1;
695   const SMDS_MeshNode* nd2 = aNodes[3];
696   aNodes[3] = aNodes[4];
697   aNodes[4] = aNodes[5];
698   aNodes[5] = nd2;
699 }
700
701 //=======================================================================
702 //function : getNodesFromTwoTria
703 //purpose  : 
704 //=======================================================================
705
706 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
707                                 const SMDS_MeshElement * theTria2,
708                                 vector< const SMDS_MeshNode*>& N1,
709                                 vector< const SMDS_MeshNode*>& N2)
710 {
711   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
712   if ( N1.size() < 6 ) return false;
713   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
714   if ( N2.size() < 6 ) return false;
715
716   int sames[3] = {-1,-1,-1};
717   int nbsames = 0;
718   int i, j;
719   for(i=0; i<3; i++) {
720     for(j=0; j<3; j++) {
721       if(N1[i]==N2[j]) {
722         sames[i] = j;
723         nbsames++;
724         break;
725       }
726     }
727   }
728   if(nbsames!=2) return false;
729   if(sames[0]>-1) {
730     shiftNodesQuadTria(N1);
731     if(sames[1]>-1) {
732       shiftNodesQuadTria(N1);
733     }
734   }
735   i = sames[0] + sames[1] + sames[2];
736   for(; i<2; i++) {
737     shiftNodesQuadTria(N2);
738   }
739   // now we receive following N1 and N2 (using numeration as in the image below)
740   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
741   // i.e. first nodes from both arrays form a new diagonal
742   return true;
743 }
744
745 //=======================================================================
746 //function : InverseDiag
747 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
748 //           but having other common link.
749 //           Return False if args are improper
750 //=======================================================================
751
752 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
753                                     const SMDS_MeshElement * theTria2 )
754 {
755   ClearLastCreated();
756
757   if ( !theTria1 || !theTria2 ||
758        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
759        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
760        theTria1->GetType() != SMDSAbs_Face ||
761        theTria2->GetType() != SMDSAbs_Face )
762     return false;
763
764   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
765       (theTria2->GetEntityType() == SMDSEntity_Triangle))
766   {
767     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
768     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
769     //    |/ |                                         | \|
770     //  B +--+ 2                                     B +--+ 2
771
772     // put nodes in array and find out indices of the same ones
773     const SMDS_MeshNode* aNodes [6];
774     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
775     int i = 0;
776     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
777     while ( it->more() ) {
778       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
779
780       if ( i > 2 ) // theTria2
781         // find same node of theTria1
782         for ( int j = 0; j < 3; j++ )
783           if ( aNodes[ i ] == aNodes[ j ]) {
784             sameInd[ j ] = i;
785             sameInd[ i ] = j;
786             break;
787           }
788       // next
789       i++;
790       if ( i == 3 ) {
791         if ( it->more() )
792           return false; // theTria1 is not a triangle
793         it = theTria2->nodesIterator();
794       }
795       if ( i == 6 && it->more() )
796         return false; // theTria2 is not a triangle
797     }
798
799     // find indices of 1,2 and of A,B in theTria1
800     int iA = -1, iB = 0, i1 = 0, i2 = 0;
801     for ( i = 0; i < 6; i++ ) {
802       if ( sameInd [ i ] == -1 ) {
803         if ( i < 3 ) i1 = i;
804         else         i2 = i;
805       }
806       else if (i < 3) {
807         if ( iA >= 0) iB = i;
808         else          iA = i;
809       }
810     }
811     // nodes 1 and 2 should not be the same
812     if ( aNodes[ i1 ] == aNodes[ i2 ] )
813       return false;
814
815     // theTria1: A->2
816     aNodes[ iA ] = aNodes[ i2 ];
817     // theTria2: B->1
818     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
819
820     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
821     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
822
823     return true;
824
825   } // end if(F1 && F2)
826
827   // check case of quadratic faces
828   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
829       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
830     return false;
831   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
832       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
833     return false;
834
835   //       5
836   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
837   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
838   //    |   / |
839   //  7 +  +  + 6
840   //    | /9  |
841   //    |/    |
842   //  4 +--+--+ 3
843   //       8
844
845   vector< const SMDS_MeshNode* > N1;
846   vector< const SMDS_MeshNode* > N2;
847   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
848     return false;
849   // now we receive following N1 and N2 (using numeration as above image)
850   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
851   // i.e. first nodes from both arrays determ new diagonal
852
853   vector< const SMDS_MeshNode*> N1new( N1.size() );
854   vector< const SMDS_MeshNode*> N2new( N2.size() );
855   N1new.back() = N1.back(); // central node of biquadratic
856   N2new.back() = N2.back();
857   N1new[0] = N1[0];  N2new[0] = N1[0];
858   N1new[1] = N2[0];  N2new[1] = N1[1];
859   N1new[2] = N2[1];  N2new[2] = N2[0];
860   N1new[3] = N1[4];  N2new[3] = N1[3];
861   N1new[4] = N2[3];  N2new[4] = N2[5];
862   N1new[5] = N1[5];  N2new[5] = N1[4];
863   // change nodes in faces
864   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
865   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
866
867   // move the central node of biquadratic triangle
868   SMESH_MesherHelper helper( *GetMesh() );
869   for ( int is2nd = 0; is2nd < 2; ++is2nd )
870   {
871     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
872     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
873     if ( nodes.size() < 7 )
874       continue;
875     helper.SetSubShape( tria->getshapeId() );
876     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
877     gp_Pnt xyz;
878     if ( F.IsNull() )
879     {
880       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
881               SMESH_NodeXYZ( nodes[4] ) +
882               SMESH_NodeXYZ( nodes[5] )) / 3.;
883     }
884     else
885     {
886       bool checkUV;
887       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
888                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
889                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
890       TopLoc_Location loc;
891       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
892       xyz = S->Value( uv.X(), uv.Y() );
893       xyz.Transform( loc );
894       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
895            nodes[6]->getshapeId() > 0 )
896         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
897     }
898     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
899   }
900   return true;
901 }
902
903 //=======================================================================
904 //function : findTriangles
905 //purpose  : find triangles sharing theNode1-theNode2 link
906 //=======================================================================
907
908 static bool findTriangles(const SMDS_MeshNode *    theNode1,
909                           const SMDS_MeshNode *    theNode2,
910                           const SMDS_MeshElement*& theTria1,
911                           const SMDS_MeshElement*& theTria2)
912 {
913   if ( !theNode1 || !theNode2 ) return false;
914
915   theTria1 = theTria2 = 0;
916
917   set< const SMDS_MeshElement* > emap;
918   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
919   while (it->more()) {
920     const SMDS_MeshElement* elem = it->next();
921     if ( elem->NbCornerNodes() == 3 )
922       emap.insert( elem );
923   }
924   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
925   while (it->more()) {
926     const SMDS_MeshElement* elem = it->next();
927     if ( emap.count( elem )) {
928       if ( !theTria1 )
929       {
930         theTria1 = elem;
931       }
932       else  
933       {
934         theTria2 = elem;
935         // theTria1 must be element with minimum ID
936         if ( theTria2->GetID() < theTria1->GetID() )
937           std::swap( theTria2, theTria1 );
938         return true;
939       }
940     }
941   }
942   return false;
943 }
944
945 //=======================================================================
946 //function : InverseDiag
947 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
948 //           with ones built on the same 4 nodes but having other common link.
949 //           Return false if proper faces not found
950 //=======================================================================
951
952 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
953                                     const SMDS_MeshNode * theNode2)
954 {
955   ClearLastCreated();
956
957   const SMDS_MeshElement *tr1, *tr2;
958   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
959     return false;
960
961   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
962        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
963     return false;
964
965   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
966       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
967
968     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
969     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
970     //    |/ |                                    | \|
971     //  B +--+ 2                                B +--+ 2
972
973     // put nodes in array
974     // and find indices of 1,2 and of A in tr1 and of B in tr2
975     int i, iA1 = 0, i1 = 0;
976     const SMDS_MeshNode* aNodes1 [3];
977     SMDS_ElemIteratorPtr it;
978     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
979       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
980       if ( aNodes1[ i ] == theNode1 )
981         iA1 = i; // node A in tr1
982       else if ( aNodes1[ i ] != theNode2 )
983         i1 = i;  // node 1
984     }
985     int iB2 = 0, i2 = 0;
986     const SMDS_MeshNode* aNodes2 [3];
987     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
988       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
989       if ( aNodes2[ i ] == theNode2 )
990         iB2 = i; // node B in tr2
991       else if ( aNodes2[ i ] != theNode1 )
992         i2 = i;  // node 2
993     }
994
995     // nodes 1 and 2 should not be the same
996     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
997       return false;
998
999     // tr1: A->2
1000     aNodes1[ iA1 ] = aNodes2[ i2 ];
1001     // tr2: B->1
1002     aNodes2[ iB2 ] = aNodes1[ i1 ];
1003
1004     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1005     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1006
1007     return true;
1008   }
1009
1010   // check case of quadratic faces
1011   return InverseDiag(tr1,tr2);
1012 }
1013
1014 //=======================================================================
1015 //function : getQuadrangleNodes
1016 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
1017 //           fusion of triangles tr1 and tr2 having shared link on
1018 //           theNode1 and theNode2
1019 //=======================================================================
1020
1021 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
1022                         const SMDS_MeshNode *    theNode1,
1023                         const SMDS_MeshNode *    theNode2,
1024                         const SMDS_MeshElement * tr1,
1025                         const SMDS_MeshElement * tr2 )
1026 {
1027   if( tr1->NbNodes() != tr2->NbNodes() )
1028     return false;
1029
1030   // find the 4-th node to insert into tr1
1031   const SMDS_MeshNode* n4 = 0;
1032   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1033   for ( int i = 0; !n4 && i < 3; ++i )
1034   {
1035     const SMDS_MeshNode * n = cast2Node( it->next() );
1036     bool isDiag = ( n == theNode1 || n == theNode2 );
1037     if ( !isDiag )
1038       n4 = n;
1039   }
1040
1041   // Make an array of nodes to be in a quadrangle
1042   int iNode = 0, iFirstDiag = -1;
1043   it = tr1->nodesIterator();
1044   for ( int i = 0; i < 3; ++i )
1045   {
1046     const SMDS_MeshNode * n = cast2Node( it->next() );
1047     bool isDiag = ( n == theNode1 || n == theNode2 );
1048     if ( isDiag ) {
1049       if ( iFirstDiag < 0 )
1050         iFirstDiag = iNode;
1051       else if ( iNode - iFirstDiag == 1 )
1052         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1053     }
1054     else if ( n == n4 ) {
1055       return false; // tr1 and tr2 should not have all the same nodes
1056     }
1057     theQuadNodes[ iNode++ ] = n;
1058   }
1059   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1060     theQuadNodes[ iNode ] = n4;
1061
1062   return true;
1063 }
1064
1065 //=======================================================================
1066 //function : DeleteDiag
1067 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1068 //           with a quadrangle built on the same 4 nodes.
1069 //           Return false if proper faces not found
1070 //=======================================================================
1071
1072 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1073                                    const SMDS_MeshNode * theNode2)
1074 {
1075   ClearLastCreated();
1076
1077   const SMDS_MeshElement *tr1, *tr2;
1078   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1079     return false;
1080
1081   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1082        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1083     return false;
1084
1085   SMESHDS_Mesh * aMesh = GetMeshDS();
1086
1087   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1088       (tr2->GetEntityType() == SMDSEntity_Triangle))
1089   {
1090     const SMDS_MeshNode* aNodes [ 4 ];
1091     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1092       return false;
1093
1094     const SMDS_MeshElement* newElem = 0;
1095     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1096     myLastCreatedElems.push_back(newElem);
1097     AddToSameGroups( newElem, tr1, aMesh );
1098     int aShapeId = tr1->getshapeId();
1099     if ( aShapeId )
1100       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1101
1102     aMesh->RemoveElement( tr1 );
1103     aMesh->RemoveElement( tr2 );
1104
1105     return true;
1106   }
1107
1108   // check case of quadratic faces
1109   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1110     return false;
1111   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1112     return false;
1113
1114   //       5
1115   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1116   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1117   //    |   / |
1118   //  7 +  +  + 6
1119   //    | /9  |
1120   //    |/    |
1121   //  4 +--+--+ 3
1122   //       8
1123
1124   vector< const SMDS_MeshNode* > N1;
1125   vector< const SMDS_MeshNode* > N2;
1126   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1127     return false;
1128   // now we receive following N1 and N2 (using numeration as above image)
1129   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1130   // i.e. first nodes from both arrays determ new diagonal
1131
1132   const SMDS_MeshNode* aNodes[8];
1133   aNodes[0] = N1[0];
1134   aNodes[1] = N1[1];
1135   aNodes[2] = N2[0];
1136   aNodes[3] = N2[1];
1137   aNodes[4] = N1[3];
1138   aNodes[5] = N2[5];
1139   aNodes[6] = N2[3];
1140   aNodes[7] = N1[5];
1141
1142   const SMDS_MeshElement* newElem = 0;
1143   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1144                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1145   myLastCreatedElems.push_back(newElem);
1146   AddToSameGroups( newElem, tr1, aMesh );
1147   int aShapeId = tr1->getshapeId();
1148   if ( aShapeId )
1149   {
1150     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1151   }
1152   aMesh->RemoveElement( tr1 );
1153   aMesh->RemoveElement( tr2 );
1154
1155   // remove middle node (9)
1156   GetMeshDS()->RemoveNode( N1[4] );
1157
1158   return true;
1159 }
1160
1161 //=======================================================================
1162 //function : SplitEdge
1163 //purpose  : Replace each triangle bound by theNode1-theNode2 segment with
1164 //           two triangles by connecting a node made on the link with a node opposite to the link.
1165 //=======================================================================
1166
1167 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1168                                   const SMDS_MeshNode * theNode2,
1169                                   double                thePosition)
1170 {
1171   ClearLastCreated();
1172
1173   SMESHDS_Mesh * mesh = GetMeshDS();
1174
1175   // Get triangles and segments to divide
1176
1177   std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1178   std::vector<const SMDS_MeshElement *> foundElems;
1179   if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1180     throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1181                             << theNode1->GetID() << " - " << theNode2->GetID());
1182
1183   SMESH_MesherHelper helper( *GetMesh() );
1184
1185   for ( const SMDS_MeshElement * elem : foundElems )
1186   {
1187     SMDSAbs_ElementType type = elem->GetType();
1188     switch ( type ) {
1189     case SMDSAbs_Volume:
1190       throw SALOME_Exception( "Can't split an edge of a volume");
1191       break;
1192
1193     case SMDSAbs_Face:
1194       if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1195         throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1196       if ( elem->IsQuadratic() )
1197       {
1198         helper.SetIsQuadratic( true );
1199         helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1200         helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1201       }
1202       break;
1203
1204     case SMDSAbs_Edge:
1205       if ( elem->IsQuadratic() )
1206       {
1207         helper.SetIsQuadratic( true );
1208         helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1209       }
1210       break;
1211     default:;
1212     }
1213   }
1214
1215   // Make a new node
1216
1217   const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1218
1219   gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1220                         SMESH_NodeXYZ( theNode2 ) * thePosition );
1221
1222   const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1223   if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1224   {
1225     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1226     double  tol = 100 * helper.MaxTolerance( S );
1227     gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1228     if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1229     {
1230       newNodeXYZ = surface->Value( uv );
1231       if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1232         nPos->SetParameters( uv.X(), uv.Y() );
1233     }
1234   }
1235   if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1236   {
1237     mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1238     double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1239     helper.ToFixNodeParameters( true );
1240     if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1241       newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1242   }
1243   mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1244
1245   // Split triangles and segments
1246
1247   std::vector<const SMDS_MeshNode *> nodes( 7 );
1248   for ( const SMDS_MeshElement * elem : foundElems )
1249   {
1250     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1251     nodes.resize( elem->NbCornerNodes() + 1 );
1252     nodes.back() = nodes[0];
1253
1254     smIdType id = elem->GetID();
1255     int shapeID = elem->GetShapeID();
1256
1257     const SMDS_MeshNode* centralNode = nullptr;
1258     if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1259       centralNode = elem->GetNode( 6 );
1260
1261     mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1262     if ( centralNode )
1263       mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1264
1265     for ( size_t i = 1; i < nodes.size(); ++i )
1266     {
1267       const SMDS_MeshNode* n1 = nodes[i-1];
1268       const SMDS_MeshNode* n2 = nodes[i];
1269       const SMDS_MeshElement* newElem;
1270       if ( nodes.size() == 4 ) //    triangle
1271       {
1272         bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1273         bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1274         if ( isDiag1 && isDiag2 )
1275           continue;
1276
1277         newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1278       }
1279       else //    segment
1280       {
1281         newElem = helper.AddEdge( n1, nodeOnLink, id );
1282       }
1283       myLastCreatedElems.push_back( newElem );
1284       AddToSameGroups( newElem, elem, mesh );
1285       if ( shapeID )
1286         mesh->SetMeshElementOnShape( newElem, shapeID );
1287       id = 0;
1288     }
1289   }
1290   return;
1291 }
1292
1293 //=======================================================================
1294 //function : SplitFace
1295 //purpose  : Split a face into triangles each formed by two nodes of the 
1296 //           face and a new node added at the given coordinates.
1297 //=======================================================================
1298
1299 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1300                                   double                   theX,
1301                                   double                   theY,
1302                                   double                   theZ )
1303 {
1304   ClearLastCreated();
1305
1306   if ( !theFace )
1307     throw SALOME_Exception("Null face given");
1308   if ( theFace->GetType() != SMDSAbs_Face )
1309     throw SALOME_Exception("Not a face given");
1310
1311   SMESHDS_Mesh * mesh = GetMeshDS();
1312
1313   SMESH_MesherHelper helper( *GetMesh() );
1314   if ( theFace->IsQuadratic() )
1315   {
1316     helper.SetIsQuadratic( true );
1317     helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1318   }
1319   const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1320   helper.SetSubShape( shape );
1321   helper.SetElementsOnShape( true );
1322
1323   // Make a new node
1324
1325   const SMDS_MeshNode* centralNode = nullptr;
1326   if (      theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1327     centralNode = theFace->GetNode( 6 );
1328   else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1329     centralNode = theFace->GetNode( 8 );
1330
1331   if ( centralNode )
1332   {
1333     helper.SetIsBiQuadratic( true );
1334     mesh->MoveNode( centralNode, theX, theY, theZ );
1335   }
1336   else
1337     centralNode = helper.AddNode( theX, theY, theZ );
1338
1339
1340   // Split theFace
1341
1342   std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1343   nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1344   nodes.resize( theFace->NbCornerNodes() + 1 );
1345   nodes.back() = nodes[0];
1346
1347   smIdType id = theFace->GetID();
1348   int shapeID = theFace->GetShapeID();
1349
1350   mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1351
1352   for ( size_t i = 1; i < nodes.size(); ++i )
1353   {
1354     const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1355
1356     myLastCreatedElems.push_back( newElem );
1357     AddToSameGroups( newElem, theFace, mesh );
1358     if ( shapeID )
1359       mesh->SetMeshElementOnShape( newElem, shapeID );
1360     id = 0;
1361   }
1362   return;
1363 }
1364
1365 //=======================================================================
1366 //function : Reorient
1367 //purpose  : Reverse theElement orientation
1368 //=======================================================================
1369
1370 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1371 {
1372   ClearLastCreated();
1373
1374   if (!theElem)
1375     return false;
1376   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1377   if ( !it || !it->more() )
1378     return false;
1379
1380   const SMDSAbs_ElementType type = theElem->GetType();
1381   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1382     return false;
1383
1384   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1385   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1386   {
1387     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1388     if (!aPolyedre) {
1389       MESSAGE("Warning: bad volumic element");
1390       return false;
1391     }
1392     SMDS_VolumeTool vTool( aPolyedre );
1393     const int nbFaces = vTool.NbFaces();
1394     vector<int> quantities( nbFaces );
1395     vector<const SMDS_MeshNode *> poly_nodes;
1396
1397     // check if all facets are oriented equally
1398     bool sameOri = true;
1399     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1400     for (int iface = 0; iface < nbFaces; iface++)
1401     {
1402       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1403       if ( facetOri[ iface ] != facetOri[ 0 ])
1404         sameOri = false;
1405     }
1406
1407     // reverse faces of the polyhedron
1408     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1409     poly_nodes.reserve( vTool.NbNodes() );
1410     for ( int iface = 0; iface < nbFaces; iface++ )
1411     {
1412       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1413       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1414       bool toReverse = ( facetOri[ iface ] != neededOri );
1415
1416       quantities[ iface ] = nbFaceNodes;
1417
1418       if ( toReverse )
1419         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1420           poly_nodes.push_back( nodes[ inode ]);
1421       else
1422         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1423     }
1424     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1425   }
1426   else // other elements
1427   {
1428     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1429     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1430     if ( interlace.empty() )
1431     {
1432       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1433     }
1434     else
1435     {
1436       SMDS_MeshCell::applyInterlace( interlace, nodes );
1437     }
1438     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1439   }
1440   return false;
1441 }
1442
1443 //================================================================================
1444 /*!
1445  * \brief Reorient faces.
1446  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1447  * \param theDirection - desired direction of normal of \a theRefFaces.
1448  *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1449  * \param theRefFaces - correctly oriented faces whose orientation defines
1450  *        orientation of other faces.
1451  * \return number of reoriented faces.
1452  */
1453 //================================================================================
1454
1455 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
1456                                   const gp_Vec&       theDirection,
1457                                   TIDSortedElemSet &  theRefFaces,
1458                                   bool                theAllowNonManifold )
1459 {
1460   int nbReori = 0;
1461
1462   if ( theFaces.empty() )
1463   {
1464     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1465     while ( fIt->more() )
1466       theFaces.insert( theFaces.end(), fIt->next() );
1467
1468     if ( theFaces.empty() )
1469       return nbReori;
1470   }
1471
1472   // orient theRefFaces according to theDirection
1473   if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1474     for ( const SMDS_MeshElement* refFace : theRefFaces )
1475     {
1476       gp_XYZ normal;
1477       SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1478       if ( normal * theDirection.XYZ() < 0 )
1479         nbReori += Reorient( refFace );
1480     }
1481
1482   // mark reference faces
1483   GetMeshDS()->SetAllCellsNotMarked();
1484   for ( const SMDS_MeshElement* refFace : theRefFaces )
1485     refFace->setIsMarked( true );
1486
1487   // erase reference faces from theFaces
1488   for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1489     if ( (*fIt)->isMarked() )
1490       fIt = theFaces.erase( fIt );
1491     else
1492       ++fIt;
1493
1494   if ( theRefFaces.empty() )
1495   {
1496     theRefFaces.insert( *theFaces.begin() );
1497     theFaces.erase( theFaces.begin() );
1498   }
1499
1500   // Orient theFaces
1501
1502   // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1503   //   theFaces.erase( theFace );
1504
1505   int nodeInd1, nodeInd2;
1506   const SMDS_MeshElement*           refFace, *otherFace;
1507   vector< const SMDS_MeshElement* > facesNearLink;
1508   vector< std::pair< int, int > >   nodeIndsOfFace;
1509   TIDSortedElemSet                  avoidSet, emptySet;
1510   NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1511
1512   while ( !theRefFaces.empty() )
1513   {
1514     auto refFaceIt = theRefFaces.begin();
1515     refFace = *refFaceIt;
1516     theRefFaces.erase( refFaceIt );
1517
1518     avoidSet.clear();
1519     avoidSet.insert( refFace );
1520
1521     NLink link( refFace->GetNode( 0 ), nullptr );
1522
1523     const int nbNodes = refFace->NbCornerNodes();
1524     for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1525     {
1526       link.second = refFace->GetNode(( i+1 ) % nbNodes );
1527       bool isLinkVisited = checkedLinks.Contains( link );
1528       if ( isLinkVisited )
1529       {
1530         // link has already been checked and won't be encountered more
1531         // if the group (theFaces) is manifold
1532         //checkedLinks.erase( linkIt_isNew.first );
1533       }
1534       else
1535       {
1536         checkedLinks.Add( link );
1537
1538         facesNearLink.clear();
1539         nodeIndsOfFace.clear();
1540         TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1541
1542         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1543                                                              emptySet, avoidSet,
1544                                                              &nodeInd1, &nodeInd2 )))
1545         {
1546           if (( otherFace->isMarked() ) || // ref face
1547               (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1548           {
1549             facesNearLink.push_back( otherFace );
1550             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1551           }
1552           avoidSet.insert( otherFace );
1553         }
1554         if ( facesNearLink.size() > 1 )
1555         {
1556           // NON-MANIFOLD mesh shell !
1557           if ( !theAllowNonManifold )
1558           {
1559             throw SALOME_Exception("Non-manifold topology of groups");
1560           }
1561           // select a face most co-directed with refFace,
1562           // other faces won't be visited this time
1563           gp_XYZ NF, NOF;
1564           SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1565           double proj, maxProj = -1;
1566           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1567           {
1568             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1569             if (( proj = Abs( NF * NOF )) > maxProj )
1570             {
1571               maxProj = proj;
1572               otherFace = facesNearLink[i];
1573               nodeInd1  = nodeIndsOfFace[i].first;
1574               nodeInd2  = nodeIndsOfFace[i].second;
1575             }
1576           }
1577           // not to visit rejected faces
1578           // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1579           //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1580           //     visitedFaces.insert( facesNearLink[i] );
1581         }
1582         else if ( facesNearLink.size() == 1 )
1583         {
1584           otherFace = facesNearLink[0];
1585           nodeInd1  = nodeIndsOfFace.back().first;
1586           nodeInd2  = nodeIndsOfFace.back().second;
1587         }
1588         if ( otherFace )
1589         {
1590           // link must be reverse in otherFace if orientation of otherFace
1591           // is same as that of refFace
1592           if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1593           {
1594             if ( otherFace->isMarked() )
1595               throw SALOME_Exception("Different orientation of reference faces");
1596             nbReori += Reorient( otherFace );
1597           }
1598           if ( !otherFace->isMarked() )
1599           {
1600             theRefFaces.insert( otherFace );
1601             if ( objFaceIt != theFaces.end() )
1602               theFaces.erase( objFaceIt );
1603           }
1604         }
1605       }
1606       link.first = link.second; // reverse the link
1607
1608     } // loop on links of refFace
1609
1610     if ( theRefFaces.empty() && !theFaces.empty() )
1611     {
1612       theRefFaces.insert( *theFaces.begin() );
1613       theFaces.erase( theFaces.begin() );
1614     }
1615
1616   } // while ( !theRefFaces.empty() )
1617
1618   return nbReori;
1619 }
1620
1621 //================================================================================
1622 /*!
1623  * \brief Reorient faces basing on orientation of adjacent volumes.
1624  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1625  * \param theVolumes - reference volumes.
1626  * \param theOutsideNormal - to orient faces to have their normal
1627  *        pointing either \a outside or \a inside the adjacent volumes.
1628  * \return number of reoriented faces.
1629  */
1630 //================================================================================
1631
1632 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1633                                       TIDSortedElemSet & theVolumes,
1634                                       const bool         theOutsideNormal)
1635 {
1636   int nbReori = 0;
1637
1638   SMDS_ElemIteratorPtr faceIt;
1639   if ( theFaces.empty() )
1640     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1641   else
1642     faceIt = SMESHUtils::elemSetIterator( theFaces );
1643
1644   vector< const SMDS_MeshNode* > faceNodes;
1645   TIDSortedElemSet checkedVolumes;
1646   set< const SMDS_MeshNode* > faceNodesSet;
1647   SMDS_VolumeTool volumeTool;
1648
1649   while ( faceIt->more() ) // loop on given faces
1650   {
1651     const SMDS_MeshElement* face = faceIt->next();
1652     if ( face->GetType() != SMDSAbs_Face )
1653       continue;
1654
1655     const size_t nbCornersNodes = face->NbCornerNodes();
1656     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1657
1658     checkedVolumes.clear();
1659     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1660     while ( vIt->more() )
1661     {
1662       const SMDS_MeshElement* volume = vIt->next();
1663
1664       if ( !checkedVolumes.insert( volume ).second )
1665         continue;
1666       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1667         continue;
1668
1669       // is volume adjacent?
1670       bool allNodesCommon = true;
1671       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1672         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1673       if ( !allNodesCommon )
1674         continue;
1675
1676       // get nodes of a corresponding volume facet
1677       faceNodesSet.clear();
1678       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1679       volumeTool.Set( volume );
1680       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1681       if ( facetID < 0 ) continue;
1682       volumeTool.SetExternalNormal();
1683       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1684
1685       // compare order of faceNodes and facetNodes
1686       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1687       int iNN[2];
1688       for ( int i = 0; i < 2; ++i )
1689       {
1690         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1691         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1692           if ( faceNodes[ iN ] == n )
1693           {
1694             iNN[ i ] = iN;
1695             break;
1696           }
1697       }
1698       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1699       if ( isOutside != theOutsideNormal )
1700         nbReori += Reorient( face );
1701     }
1702   }  // loop on given faces
1703
1704   return nbReori;
1705 }
1706
1707 //=======================================================================
1708 //function : getBadRate
1709 //purpose  :
1710 //=======================================================================
1711
1712 static double getBadRate (const SMDS_MeshElement*               theElem,
1713                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1714 {
1715   SMESH::Controls::TSequenceOfXYZ P;
1716   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1717     return 1e100;
1718   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1719   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1720 }
1721
1722 //=======================================================================
1723 //function : QuadToTri
1724 //purpose  : Cut quadrangles into triangles.
1725 //           theCrit is used to select a diagonal to cut
1726 //=======================================================================
1727
1728 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1729                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1730 {
1731   ClearLastCreated();
1732
1733   if ( !theCrit.get() )
1734     return false;
1735
1736   SMESHDS_Mesh *       aMesh = GetMeshDS();
1737   Handle(Geom_Surface) surface;
1738   SMESH_MesherHelper   helper( *GetMesh() );
1739
1740   myLastCreatedElems.reserve( theElems.size() * 2 );
1741
1742   TIDSortedElemSet::iterator itElem;
1743   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1744   {
1745     const SMDS_MeshElement* elem = *itElem;
1746     if ( !elem || elem->GetType() != SMDSAbs_Face )
1747       continue;
1748     if ( elem->NbCornerNodes() != 4 )
1749       continue;
1750
1751     // retrieve element nodes
1752     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1753
1754     // compare two sets of possible triangles
1755     double aBadRate1, aBadRate2; // to what extent a set is bad
1756     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1757     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1758     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1759
1760     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1761     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1762     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1763
1764     const int aShapeId = FindShape( elem );
1765     const SMDS_MeshElement* newElem1 = 0;
1766     const SMDS_MeshElement* newElem2 = 0;
1767
1768     if ( !elem->IsQuadratic() ) // split linear quadrangle
1769     {
1770       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1771       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1772       if ( aBadRate1 <= aBadRate2 ) {
1773         // tr1 + tr2 is better
1774         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1775         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1776       }
1777       else {
1778         // tr3 + tr4 is better
1779         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1780         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1781       }
1782     }
1783     else // split quadratic quadrangle
1784     {
1785       helper.SetIsQuadratic( true );
1786       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1787
1788       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1789       if ( aNodes.size() == 9 )
1790       {
1791         helper.SetIsBiQuadratic( true );
1792         if ( aBadRate1 <= aBadRate2 )
1793           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1794         else
1795           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1796       }
1797       // create a new element
1798       if ( aBadRate1 <= aBadRate2 ) {
1799         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1800         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1801       }
1802       else {
1803         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1804         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1805       }
1806     } // quadratic case
1807
1808     // care of a new element
1809
1810     myLastCreatedElems.push_back(newElem1);
1811     myLastCreatedElems.push_back(newElem2);
1812     AddToSameGroups( newElem1, elem, aMesh );
1813     AddToSameGroups( newElem2, elem, aMesh );
1814
1815     // put a new triangle on the same shape
1816     if ( aShapeId )
1817       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1818     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1819
1820     aMesh->RemoveElement( elem );
1821   }
1822   return true;
1823 }
1824
1825 //=======================================================================
1826 /*!
1827  * \brief Split each of given quadrangles into 4 triangles.
1828  * \param theElems - The faces to be split. If empty all faces are split.
1829  */
1830 //=======================================================================
1831
1832 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1833 {
1834   ClearLastCreated();
1835   myLastCreatedElems.reserve( theElems.size() * 4 );
1836
1837   SMESH_MesherHelper helper( *GetMesh() );
1838   helper.SetElementsOnShape( true );
1839
1840   // get standalone groups of faces
1841   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1842   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1843     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1844       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1845         allFaceGroups.push_back( & group->SMDSGroup() );
1846
1847   bool   checkUV;
1848   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1849   gp_XYZ xyz[9];
1850   vector< const SMDS_MeshNode* > nodes;
1851   SMESHDS_SubMesh*               subMeshDS = 0;
1852   TopoDS_Face                    F;
1853   Handle(Geom_Surface)           surface;
1854   TopLoc_Location                loc;
1855
1856   SMDS_ElemIteratorPtr faceIt;
1857   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1858   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1859
1860   while ( faceIt->more() )
1861   {
1862     const SMDS_MeshElement* quad = faceIt->next();
1863     if ( !quad || quad->NbCornerNodes() != 4 )
1864       continue;
1865
1866     // get a surface the quad is on
1867
1868     if ( quad->getshapeId() < 1 )
1869     {
1870       F.Nullify();
1871       helper.SetSubShape( 0 );
1872       subMeshDS = 0;
1873     }
1874     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1875     {
1876       helper.SetSubShape( quad->getshapeId() );
1877       if ( !helper.GetSubShape().IsNull() &&
1878            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1879       {
1880         F = TopoDS::Face( helper.GetSubShape() );
1881         surface = BRep_Tool::Surface( F, loc );
1882         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1883       }
1884       else
1885       {
1886         helper.SetSubShape( 0 );
1887         subMeshDS = 0;
1888       }
1889     }
1890
1891     // create a central node
1892
1893     const SMDS_MeshNode* nCentral;
1894     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1895
1896     if ( nodes.size() == 9 )
1897     {
1898       nCentral = nodes.back();
1899     }
1900     else
1901     {
1902       size_t iN = 0;
1903       if ( F.IsNull() )
1904       {
1905         for ( ; iN < nodes.size(); ++iN )
1906           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1907
1908         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1909           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1910
1911         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1912                                    xyz[0], xyz[1], xyz[2], xyz[3],
1913                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1914       }
1915       else
1916       {
1917         for ( ; iN < nodes.size(); ++iN )
1918           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1919
1920         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1921           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1922
1923         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1924                                   uv[0], uv[1], uv[2], uv[3],
1925                                   uv[4], uv[5], uv[6], uv[7] );
1926
1927         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1928         xyz[ 8 ] = p.XYZ();
1929       }
1930
1931       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1932                                  uv[8].X(), uv[8].Y() );
1933       myLastCreatedNodes.push_back( nCentral );
1934     }
1935
1936     helper.SetIsQuadratic  ( nodes.size() > 4 );
1937     helper.SetIsBiQuadratic( nodes.size() == 9 );
1938     if ( helper.GetIsQuadratic() )
1939       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1940
1941     // select groups to update
1942     faceGroups.clear();
1943     for ( SMDS_MeshGroup* group : allFaceGroups )
1944       if ( group->Remove( quad ))
1945         faceGroups.push_back( group );
1946
1947     // create 4 triangles
1948
1949     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1950
1951     for ( int i = 0; i < 4; ++i )
1952     {
1953       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1954                                                nodes[(i+1)%4],
1955                                                nCentral );
1956       myLastCreatedElems.push_back( tria );
1957       for ( SMDS_MeshGroup* group : faceGroups )
1958         group->Add( tria );
1959     }
1960   }
1961 }
1962
1963 //=======================================================================
1964 //function : BestSplit
1965 //purpose  : Find better diagonal for cutting.
1966 //=======================================================================
1967
1968 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1969                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1970 {
1971   ClearLastCreated();
1972
1973   if (!theCrit.get())
1974     return -1;
1975
1976   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1977     return -1;
1978
1979   if( theQuad->NbNodes()==4 ||
1980       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1981
1982     // retrieve element nodes
1983     const SMDS_MeshNode* aNodes [4];
1984     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1985     int i = 0;
1986     //while (itN->more())
1987     while (i<4) {
1988       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1989     }
1990     // compare two sets of possible triangles
1991     double aBadRate1, aBadRate2; // to what extent a set is bad
1992     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1993     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1994     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1995
1996     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1997     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1998     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1999     // for MaxElementLength2D functor we return minimum diagonal for splitting,
2000     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
2001     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2002       return 1; // diagonal 1-3
2003
2004     return 2; // diagonal 2-4
2005   }
2006   return -1;
2007 }
2008
2009 namespace
2010 {
2011   // Methods of splitting volumes into tetra
2012
2013   const int theHexTo5_1[5*4+1] =
2014     {
2015       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
2016     };
2017   const int theHexTo5_2[5*4+1] =
2018     {
2019       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
2020     };
2021   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2022
2023   const int theHexTo6_1[6*4+1] =
2024     {
2025       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
2026     };
2027   const int theHexTo6_2[6*4+1] =
2028     {
2029       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
2030     };
2031   const int theHexTo6_3[6*4+1] =
2032     {
2033       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
2034     };
2035   const int theHexTo6_4[6*4+1] =
2036     {
2037       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
2038     };
2039   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2040
2041   const int thePyraTo2_1[2*4+1] =
2042     {
2043       0, 1, 2, 4,    0, 2, 3, 4,   -1
2044     };
2045   const int thePyraTo2_2[2*4+1] =
2046     {
2047       1, 2, 3, 4,    1, 3, 0, 4,   -1
2048     };
2049   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2050
2051   const int thePentaTo3_1[3*4+1] =
2052     {
2053       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
2054     };
2055   const int thePentaTo3_2[3*4+1] =
2056     {
2057       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
2058     };
2059   const int thePentaTo3_3[3*4+1] =
2060     {
2061       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
2062     };
2063   const int thePentaTo3_4[3*4+1] =
2064     {
2065       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
2066     };
2067   const int thePentaTo3_5[3*4+1] =
2068     {
2069       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
2070     };
2071   const int thePentaTo3_6[3*4+1] =
2072     {
2073       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
2074     };
2075   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2076                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2077
2078   // Methods of splitting hexahedron into prisms
2079
2080   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2081     {
2082       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
2083     };
2084   const int theHexTo4Prisms_LR[6*4+1] = // left-right
2085     {
2086       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
2087     };
2088   const int theHexTo4Prisms_FB[6*4+1] = // front-back
2089     {
2090       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
2091     };
2092
2093   const int theHexTo2Prisms_BT_1[6*2+1] =
2094     {
2095       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
2096     };
2097   const int theHexTo2Prisms_BT_2[6*2+1] =
2098     {
2099       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
2100     };
2101   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2102
2103   const int theHexTo2Prisms_LR_1[6*2+1] =
2104     {
2105       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2106     };
2107   const int theHexTo2Prisms_LR_2[6*2+1] =
2108     {
2109       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2110     };
2111   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2112
2113   const int theHexTo2Prisms_FB_1[6*2+1] =
2114     {
2115       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
2116     };
2117   const int theHexTo2Prisms_FB_2[6*2+1] =
2118     {
2119       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
2120     };
2121   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2122
2123
2124   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2125   {
2126     int _n1, _n2, _n3;
2127     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2128     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2129     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
2130                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2131   };
2132   struct TSplitMethod
2133   {
2134     int        _nbSplits;
2135     int        _nbCorners;
2136     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2137     bool       _baryNode;     //!< additional node is to be created at cell barycenter
2138     bool       _ownConn;      //!< to delete _connectivity in destructor
2139     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2140
2141     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2142       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2143     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2144     TSplitMethod(const TSplitMethod &splitMethod)
2145       : _nbSplits(splitMethod._nbSplits),
2146         _nbCorners(splitMethod._nbCorners),
2147         _baryNode(splitMethod._baryNode),
2148         _ownConn(splitMethod._ownConn),
2149         _faceBaryNode(splitMethod._faceBaryNode)
2150     {
2151       _connectivity = splitMethod._connectivity;
2152       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2153       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2154     }
2155     bool hasFacet( const TTriangleFacet& facet ) const
2156     {
2157       if ( _nbCorners == 4 )
2158       {
2159         const int* tetConn = _connectivity;
2160         for ( ; tetConn[0] >= 0; tetConn += 4 )
2161           if (( facet.contains( tetConn[0] ) +
2162                 facet.contains( tetConn[1] ) +
2163                 facet.contains( tetConn[2] ) +
2164                 facet.contains( tetConn[3] )) == 3 )
2165             return true;
2166       }
2167       else // prism, _nbCorners == 6
2168       {
2169         const int* prismConn = _connectivity;
2170         for ( ; prismConn[0] >= 0; prismConn += 6 )
2171         {
2172           if (( facet.contains( prismConn[0] ) &&
2173                 facet.contains( prismConn[1] ) &&
2174                 facet.contains( prismConn[2] ))
2175               ||
2176               ( facet.contains( prismConn[3] ) &&
2177                 facet.contains( prismConn[4] ) &&
2178                 facet.contains( prismConn[5] )))
2179             return true;
2180         }
2181       }
2182       return false;
2183     }
2184   };
2185
2186   //=======================================================================
2187   /*!
2188    * \brief return TSplitMethod for the given element to split into tetrahedra
2189    */
2190   //=======================================================================
2191
2192   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2193   {
2194     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2195
2196     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2197     // an edge and a face barycenter; tertaherdons are based on triangles and
2198     // a volume barycenter
2199     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2200
2201     // Find out how adjacent volumes are split
2202
2203     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2204     int hasAdjacentSplits = 0, maxTetConnSize = 0;
2205     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2206     {
2207       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2208       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2209       if ( nbNodes < 4 ) continue;
2210
2211       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2212       const int* nInd = vol.GetFaceNodesIndices( iF );
2213       if ( nbNodes == 4 )
2214       {
2215         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2216         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2217         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2218         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2219       }
2220       else
2221       {
2222         int iCom = 0; // common node of triangle faces to split into
2223         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2224         {
2225           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
2226                                nInd[ iQ * ( (iCom+1)%nbNodes )],
2227                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
2228           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
2229                                nInd[ iQ * ( (iCom+2)%nbNodes )],
2230                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
2231           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2232           {
2233             triaSplits.push_back( t012 );
2234             triaSplits.push_back( t023 );
2235             break;
2236           }
2237         }
2238       }
2239       if ( !triaSplits.empty() )
2240         hasAdjacentSplits = true;
2241     }
2242
2243     // Among variants of split method select one compliant with adjacent volumes
2244
2245     TSplitMethod method;
2246     if ( !vol.Element()->IsPoly() && !is24TetMode )
2247     {
2248       int nbVariants = 2, nbTet = 0;
2249       const int** connVariants = 0;
2250       switch ( vol.Element()->GetEntityType() )
2251       {
2252       case SMDSEntity_Hexa:
2253       case SMDSEntity_Quad_Hexa:
2254       case SMDSEntity_TriQuad_Hexa:
2255         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2256           connVariants = theHexTo5, nbTet = 5;
2257         else
2258           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2259         break;
2260       case SMDSEntity_Pyramid:
2261       case SMDSEntity_Quad_Pyramid:
2262         connVariants = thePyraTo2;  nbTet = 2;
2263         break;
2264       case SMDSEntity_Penta:
2265       case SMDSEntity_Quad_Penta:
2266       case SMDSEntity_BiQuad_Penta:
2267         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2268         break;
2269       default:
2270         nbVariants = 0;
2271       }
2272       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2273       {
2274         // check method compliance with adjacent tetras,
2275         // all found splits must be among facets of tetras described by this method
2276         method = TSplitMethod( nbTet, connVariants[variant] );
2277         if ( hasAdjacentSplits && method._nbSplits > 0 )
2278         {
2279           bool facetCreated = true;
2280           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2281           {
2282             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2283             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2284               facetCreated = method.hasFacet( *facet );
2285           }
2286           if ( !facetCreated )
2287             method = TSplitMethod(0); // incompatible method
2288         }
2289       }
2290     }
2291     if ( method._nbSplits < 1 )
2292     {
2293       // No standard method is applicable, use a generic solution:
2294       // each facet of a volume is split into triangles and
2295       // each of triangles and a volume barycenter form a tetrahedron.
2296
2297       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2298
2299       int* connectivity = new int[ maxTetConnSize + 1 ];
2300       method._connectivity = connectivity;
2301       method._ownConn = true;
2302       method._baryNode = !isHex27; // to create central node or not
2303
2304       int connSize = 0;
2305       int baryCenInd = vol.NbNodes() - int( isHex27 );
2306       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2307       {
2308         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2309         const int*   nInd = vol.GetFaceNodesIndices( iF );
2310         // find common node of triangle facets of tetra to create
2311         int iCommon = 0; // index in linear numeration
2312         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2313         if ( !triaSplits.empty() )
2314         {
2315           // by found facets
2316           const TTriangleFacet* facet = &triaSplits.front();
2317           for ( ; iCommon < nbNodes-1 ; ++iCommon )
2318             if ( facet->contains( nInd[ iQ * iCommon ]) &&
2319                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2320               break;
2321         }
2322         else if ( nbNodes > 3 && !is24TetMode )
2323         {
2324           // find the best method of splitting into triangles by aspect ratio
2325           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2326           map< double, int > badness2iCommon;
2327           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2328           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2329           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2330           {
2331             double badness = 0;
2332             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2333             {
2334               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2335                                       nodes[ iQ*((iLast-1)%nbNodes)],
2336                                       nodes[ iQ*((iLast  )%nbNodes)]);
2337               badness += getBadRate( &tria, aspectRatio );
2338             }
2339             badness2iCommon.insert( make_pair( badness, iCommon ));
2340           }
2341           // use iCommon with lowest badness
2342           iCommon = badness2iCommon.begin()->second;
2343         }
2344         if ( iCommon >= nbNodes )
2345           iCommon = 0; // something wrong
2346
2347         // fill connectivity of tetrahedra based on a current face
2348         int nbTet = nbNodes - 2;
2349         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2350         {
2351           int faceBaryCenInd;
2352           if ( isHex27 )
2353           {
2354             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2355             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2356           }
2357           else
2358           {
2359             method._faceBaryNode[ iF ] = 0;
2360             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2361           }
2362           nbTet = nbNodes;
2363           for ( int i = 0; i < nbTet; ++i )
2364           {
2365             int i1 = i, i2 = (i+1) % nbNodes;
2366             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2367             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2368             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2369             connectivity[ connSize++ ] = faceBaryCenInd;
2370             connectivity[ connSize++ ] = baryCenInd;
2371           }
2372         }
2373         else
2374         {
2375           for ( int i = 0; i < nbTet; ++i )
2376           {
2377             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2378             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2379             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2380             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2381             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2382             connectivity[ connSize++ ] = baryCenInd;
2383           }
2384         }
2385         method._nbSplits += nbTet;
2386
2387       } // loop on volume faces
2388
2389       connectivity[ connSize++ ] = -1;
2390
2391     } // end of generic solution
2392
2393     return method;
2394   }
2395   //=======================================================================
2396   /*!
2397    * \brief return TSplitMethod to split haxhedron into prisms
2398    */
2399   //=======================================================================
2400
2401   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2402                                     const int        methodFlags,
2403                                     const int        facetToSplit)
2404   {
2405     TSplitMethod method;
2406
2407     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2408     // B, T, L, B, R, F
2409     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2410
2411     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2412     {
2413       static TSplitMethod to4methods[4]; // order BT, LR, FB
2414       if ( to4methods[iF]._nbSplits == 0 )
2415       {
2416         switch ( iF ) {
2417         case 0:
2418           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2419           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2420           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2421           break;
2422         case 1:
2423           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2424           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2425           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2426           break;
2427         case 2:
2428           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2429           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2430           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2431           break;
2432         default: return to4methods[3];
2433         }
2434         to4methods[iF]._nbSplits  = 4;
2435         to4methods[iF]._nbCorners = 6;
2436       }
2437       method = to4methods[iF];
2438       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2439       return method;
2440     }
2441     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2442
2443     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2444
2445     const int nbVariants = 2, nbSplits = 2;
2446     const int** connVariants = 0;
2447     switch ( iF ) {
2448     case 0: connVariants = theHexTo2Prisms_BT; break;
2449     case 1: connVariants = theHexTo2Prisms_LR; break;
2450     case 2: connVariants = theHexTo2Prisms_FB; break;
2451     default: return method;
2452     }
2453
2454     // look for prisms adjacent via facetToSplit and an opposite one
2455     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2456     {
2457       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2458       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2459       if ( nbNodes != 4 ) return method;
2460
2461       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2462       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2463       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2464       TTriangleFacet* t;
2465       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2466         t = &t012;
2467       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2468         t = &t123;
2469       else
2470         continue;
2471
2472       // there are adjacent prism
2473       for ( int variant = 0; variant < nbVariants; ++variant )
2474       {
2475         // check method compliance with adjacent prisms,
2476         // the found prism facets must be among facets of prisms described by current method
2477         method._nbSplits     = nbSplits;
2478         method._nbCorners    = 6;
2479         method._connectivity = connVariants[ variant ];
2480         if ( method.hasFacet( *t ))
2481           return method;
2482       }
2483     }
2484
2485     // No adjacent prisms. Select a variant with a best aspect ratio.
2486
2487     double badness[2] = { 0., 0. };
2488     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2489     const SMDS_MeshNode** nodes = vol.GetNodes();
2490     for ( int variant = 0; variant < nbVariants; ++variant )
2491       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2492       {
2493         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2494         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2495
2496         method._connectivity = connVariants[ variant ];
2497         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2498         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2499         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2500
2501         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2502                                 nodes[ t->_n2 ],
2503                                 nodes[ t->_n3 ] );
2504         badness[ variant ] += getBadRate( &tria, aspectRatio );
2505       }
2506     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2507
2508     method._nbSplits     = nbSplits;
2509     method._nbCorners    = 6;
2510     method._connectivity = connVariants[ iBetter ];
2511
2512     return method;
2513   }
2514
2515   //================================================================================
2516   /*!
2517    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2518    */
2519   //================================================================================
2520
2521   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2522                                        const SMDSAbs_GeometryType geom ) const
2523   {
2524     // find the tetrahedron including the three nodes of facet
2525     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2526     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2527     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2528     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2529     while ( volIt1->more() )
2530     {
2531       const SMDS_MeshElement* v = volIt1->next();
2532       if ( v->GetGeomType() != geom )
2533         continue;
2534       const int lastCornerInd = v->NbCornerNodes() - 1;
2535       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2536         continue; // medium node not allowed
2537       const int ind2 = v->GetNodeIndex( n2 );
2538       if ( ind2 < 0 || lastCornerInd < ind2 )
2539         continue;
2540       const int ind3 = v->GetNodeIndex( n3 );
2541       if ( ind3 < 0 || lastCornerInd < ind3 )
2542         continue;
2543       return true;
2544     }
2545     return false;
2546   }
2547
2548   //=======================================================================
2549   /*!
2550    * \brief A key of a face of volume
2551    */
2552   //=======================================================================
2553
2554   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2555   {
2556     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2557     {
2558       TIDSortedNodeSet sortedNodes;
2559       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2560       int nbNodes = vol.NbFaceNodes( iF );
2561       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2562       for ( int i = 0; i < nbNodes; i += iQ )
2563         sortedNodes.insert( fNodes[i] );
2564       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2565       first.first   = (*(n++))->GetID();
2566       first.second  = (*(n++))->GetID();
2567       second.first  = (*(n++))->GetID();
2568       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2569     }
2570   };
2571 } // namespace
2572
2573 //=======================================================================
2574 //function : SplitVolumes
2575 //purpose  : Split volume elements into tetrahedra or prisms.
2576 //           If facet ID < 0, element is split into tetrahedra,
2577 //           else a hexahedron is split into prisms so that the given facet is
2578 //           split into triangles
2579 //=======================================================================
2580
2581 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2582                                      const int            theMethodFlags)
2583 {
2584   SMDS_VolumeTool    volTool;
2585   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2586   fHelper.ToFixNodeParameters( true );
2587
2588   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2589   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2590
2591   SMESH_SequenceOfElemPtr newNodes, newElems;
2592
2593   // map face of volume to it's baricenrtic node
2594   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2595   double bc[3];
2596   vector<const SMDS_MeshElement* > splitVols;
2597
2598   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2599   for ( ; elem2facet != theElems.end(); ++elem2facet )
2600   {
2601     const SMDS_MeshElement* elem = elem2facet->first;
2602     const int       facetToSplit = elem2facet->second;
2603     if ( elem->GetType() != SMDSAbs_Volume )
2604       continue;
2605     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2606     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2607       continue;
2608
2609     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2610
2611     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2612                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2613                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2614     if ( splitMethod._nbSplits < 1 ) continue;
2615
2616     // find submesh to add new tetras to
2617     if ( !subMesh || !subMesh->Contains( elem ))
2618     {
2619       int shapeID = FindShape( elem );
2620       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2621       subMesh = GetMeshDS()->MeshElements( shapeID );
2622     }
2623     int iQ;
2624     if ( elem->IsQuadratic() )
2625     {
2626       iQ = 2;
2627       // add quadratic links to the helper
2628       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2629       {
2630         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2631         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2632         for ( int iN = 0; iN < nbN; iN += iQ )
2633           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2634       }
2635       helper.SetIsQuadratic( true );
2636     }
2637     else
2638     {
2639       iQ = 1;
2640       helper.SetIsQuadratic( false );
2641     }
2642     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2643                                         volTool.GetNodes() + elem->NbNodes() );
2644     helper.SetElementsOnShape( true );
2645     if ( splitMethod._baryNode )
2646     {
2647       // make a node at barycenter
2648       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2649       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2650       nodes.push_back( gcNode );
2651       newNodes.push_back( gcNode );
2652     }
2653     if ( !splitMethod._faceBaryNode.empty() )
2654     {
2655       // make or find baricentric nodes of faces
2656       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2657       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2658       {
2659         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2660           volFace2BaryNode.insert
2661           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2662         if ( !f_n->second )
2663         {
2664           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2665           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2666         }
2667         nodes.push_back( iF_n->second = f_n->second );
2668       }
2669     }
2670
2671     // make new volumes
2672     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2673     const int* volConn = splitMethod._connectivity;
2674     if ( splitMethod._nbCorners == 4 ) // tetra
2675       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2676         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2677                                                                nodes[ volConn[1] ],
2678                                                                nodes[ volConn[2] ],
2679                                                                nodes[ volConn[3] ]));
2680     else // prisms
2681       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2682         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2683                                                                nodes[ volConn[1] ],
2684                                                                nodes[ volConn[2] ],
2685                                                                nodes[ volConn[3] ],
2686                                                                nodes[ volConn[4] ],
2687                                                                nodes[ volConn[5] ]));
2688
2689     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2690
2691     // Split faces on sides of the split volume
2692
2693     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2694     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2695     {
2696       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2697       if ( nbNodes < 4 ) continue;
2698
2699       // find an existing face
2700       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2701                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2702       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2703                                                                        /*noMedium=*/false))
2704       {
2705         // make triangles
2706         helper.SetElementsOnShape( false );
2707         vector< const SMDS_MeshElement* > triangles;
2708
2709         // find submesh to add new triangles in
2710         if ( !fSubMesh || !fSubMesh->Contains( face ))
2711         {
2712           int shapeID = FindShape( face );
2713           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2714         }
2715         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2716         if ( iF_n != splitMethod._faceBaryNode.end() )
2717         {
2718           const SMDS_MeshNode *baryNode = iF_n->second;
2719           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2720           {
2721             const SMDS_MeshNode* n1 = fNodes[iN];
2722             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2723             const SMDS_MeshNode *n3 = baryNode;
2724             if ( !volTool.IsFaceExternal( iF ))
2725               swap( n2, n3 );
2726             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2727           }
2728           if ( fSubMesh ) // update position of the bary node on geometry
2729           {
2730             if ( subMesh )
2731               subMesh->RemoveNode( baryNode );
2732             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2733             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2734             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2735             {
2736               fHelper.SetSubShape( s );
2737               gp_XY uv( 1e100, 1e100 );
2738               double distXYZ[4];
2739               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2740                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2741                    uv.X() < 1e100 )
2742               {
2743                 // node is too far from the surface
2744                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2745                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2746                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2747               }
2748             }
2749           }
2750         }
2751         else
2752         {
2753           // among possible triangles create ones described by split method
2754           const int* nInd = volTool.GetFaceNodesIndices( iF );
2755           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2756           int iCom = 0; // common node of triangle faces to split into
2757           list< TTriangleFacet > facets;
2758           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2759           {
2760             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2761                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2762                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2763             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2764                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2765                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2766             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2767             {
2768               facets.push_back( t012 );
2769               facets.push_back( t023 );
2770               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2771                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2772                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2773                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2774               break;
2775             }
2776           }
2777           list< TTriangleFacet >::iterator facet = facets.begin();
2778           if ( facet == facets.end() )
2779             break;
2780           for ( ; facet != facets.end(); ++facet )
2781           {
2782             if ( !volTool.IsFaceExternal( iF ))
2783               swap( facet->_n2, facet->_n3 );
2784             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2785                                                  volNodes[ facet->_n2 ],
2786                                                  volNodes[ facet->_n3 ]));
2787           }
2788         }
2789         for ( size_t i = 0; i < triangles.size(); ++i )
2790         {
2791           if ( !triangles[ i ]) continue;
2792           if ( fSubMesh )
2793             fSubMesh->AddElement( triangles[ i ]);
2794           newElems.push_back( triangles[ i ]);
2795         }
2796         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2797         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2798
2799       } // while a face based on facet nodes exists
2800     } // loop on volume faces to split them into triangles
2801
2802     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2803
2804     if ( geomType == SMDSEntity_TriQuad_Hexa )
2805     {
2806       // remove medium nodes that could become free
2807       for ( int i = 20; i < volTool.NbNodes(); ++i )
2808         if ( volNodes[i]->NbInverseElements() == 0 )
2809           GetMeshDS()->RemoveNode( volNodes[i] );
2810     }
2811   } // loop on volumes to split
2812
2813   myLastCreatedNodes = newNodes;
2814   myLastCreatedElems = newElems;
2815 }
2816
2817 //=======================================================================
2818 //function : GetHexaFacetsToSplit
2819 //purpose  : For hexahedra that will be split into prisms, finds facets to
2820 //           split into triangles. Only hexahedra adjacent to the one closest
2821 //           to theFacetNormal.Location() are returned.
2822 //param [in,out] theHexas - the hexahedra
2823 //param [in]     theFacetNormal - facet normal
2824 //param [out]    theFacets - the hexahedra and found facet IDs
2825 //=======================================================================
2826
2827 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2828                                              const gp_Ax1&     theFacetNormal,
2829                                              TFacetOfElem &    theFacets)
2830 {
2831 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2832
2833   // Find a hexa closest to the location of theFacetNormal
2834
2835   const SMDS_MeshElement* startHex;
2836   {
2837     // get SMDS_ElemIteratorPtr on theHexas
2838     typedef const SMDS_MeshElement*                                      TValue;
2839     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2840     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2841     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2842     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2843     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2844       ( new TElemSetIter( theHexas.begin(),
2845                           theHexas.end(),
2846                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2847
2848     SMESH_ElementSearcher* searcher =
2849       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2850
2851     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2852
2853     delete searcher;
2854
2855     if ( !startHex )
2856       throw SALOME_Exception( THIS_METHOD "startHex not found");
2857   }
2858
2859   // Select a facet of startHex by theFacetNormal
2860
2861   SMDS_VolumeTool vTool( startHex );
2862   double norm[3], dot, maxDot = 0;
2863   int facetID = -1;
2864   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2865     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2866     {
2867       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2868       if ( dot > maxDot )
2869       {
2870         facetID = iF;
2871         maxDot = dot;
2872       }
2873     }
2874   if ( facetID < 0 )
2875     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2876
2877   // Fill theFacets starting from facetID of startHex
2878
2879   // facets used for searching of volumes adjacent to already treated ones
2880   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2881   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2882   TFacetMap facetsToCheck;
2883
2884   set<const SMDS_MeshNode*> facetNodes;
2885   const SMDS_MeshElement*   curHex;
2886
2887   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2888
2889   while ( startHex )
2890   {
2891     // move in two directions from startHex via facetID
2892     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2893     {
2894       curHex       = startHex;
2895       int curFacet = facetID;
2896       if ( is2nd ) // do not treat startHex twice
2897       {
2898         vTool.Set( curHex );
2899         if ( vTool.IsFreeFace( curFacet, &curHex ))
2900         {
2901           curHex = 0;
2902         }
2903         else
2904         {
2905           vTool.GetFaceNodes( curFacet, facetNodes );
2906           vTool.Set( curHex );
2907           curFacet = vTool.GetFaceIndex( facetNodes );
2908         }
2909       }
2910       while ( curHex )
2911       {
2912         // store a facet to split
2913         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2914         {
2915           theFacets.insert( make_pair( curHex, -1 ));
2916           break;
2917         }
2918         if ( !allHex && !theHexas.count( curHex ))
2919           break;
2920
2921         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2922           theFacets.insert( make_pair( curHex, curFacet ));
2923         if ( !facetIt2isNew.second )
2924           break;
2925
2926         // remember not-to-split facets in facetsToCheck
2927         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2928         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2929         {
2930           if ( iF == curFacet && iF == oppFacet )
2931             continue;
2932           TVolumeFaceKey facetKey ( vTool, iF );
2933           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2934           pair< TFacetMap::iterator, bool > it2isnew =
2935             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2936           if ( !it2isnew.second )
2937             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2938         }
2939         // pass to a volume adjacent via oppFacet
2940         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2941         {
2942           curHex = 0;
2943         }
2944         else
2945         {
2946           // get a new curFacet
2947           vTool.GetFaceNodes( oppFacet, facetNodes );
2948           vTool.Set( curHex );
2949           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2950         }
2951       }
2952     } // move in two directions from startHex via facetID
2953
2954     // Find a new startHex by facetsToCheck
2955
2956     startHex = 0;
2957     facetID  = -1;
2958     TFacetMap::iterator fIt = facetsToCheck.begin();
2959     while ( !startHex && fIt != facetsToCheck.end() )
2960     {
2961       const TElemFacets&  elemFacets = fIt->second;
2962       const SMDS_MeshElement*    hex = elemFacets.first->first;
2963       int                 splitFacet = elemFacets.first->second;
2964       int               lateralFacet = elemFacets.second;
2965       facetsToCheck.erase( fIt );
2966       fIt = facetsToCheck.begin();
2967
2968       vTool.Set( hex );
2969       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2970            curHex->GetGeomType() != SMDSGeom_HEXA )
2971         continue;
2972       if ( !allHex && !theHexas.count( curHex ))
2973         continue;
2974
2975       startHex = curHex;
2976
2977       // find a facet of startHex to split
2978
2979       set<const SMDS_MeshNode*> lateralNodes;
2980       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2981       vTool.GetFaceNodes( splitFacet,   facetNodes );
2982       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2983       vTool.Set( startHex );
2984       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2985
2986       // look for a facet of startHex having common nodes with facetNodes
2987       // but not lateralFacet
2988       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2989       {
2990         if ( iF == lateralFacet )
2991           continue;
2992         int nbCommonNodes = 0;
2993         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2994         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2995           nbCommonNodes += facetNodes.count( nn[ iN ]);
2996
2997         if ( nbCommonNodes >= 2 )
2998         {
2999           facetID = iF;
3000           break;
3001         }
3002       }
3003       if ( facetID < 0 )
3004         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3005     }
3006   } //   while ( startHex )
3007
3008   return;
3009 }
3010
3011 namespace
3012 {
3013   //================================================================================
3014   /*!
3015    * \brief Selects nodes of several elements according to a given interlace
3016    *  \param [in] srcNodes - nodes to select from
3017    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
3018    *  \param [in] interlace - indices of nodes for all elements
3019    *  \param [in] nbElems - nb of elements
3020    *  \param [in] nbNodes - nb of nodes in each element
3021    *  \param [in] mesh - the mesh
3022    *  \param [out] elemQueue - a list to push elements found by the selected nodes
3023    *  \param [in] type - type of elements to look for
3024    */
3025   //================================================================================
3026
3027   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3028                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
3029                     const int*                            interlace,
3030                     const int                             nbElems,
3031                     const int                             nbNodes,
3032                     SMESHDS_Mesh*                         mesh = 0,
3033                     list< const SMDS_MeshElement* >*      elemQueue=0,
3034                     SMDSAbs_ElementType                   type=SMDSAbs_All)
3035   {
3036     for ( int iE = 0; iE < nbElems; ++iE )
3037     {
3038       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3039       const int*                         select = & interlace[iE*nbNodes];
3040       elemNodes.resize( nbNodes );
3041       for ( int iN = 0; iN < nbNodes; ++iN )
3042         elemNodes[iN] = srcNodes[ select[ iN ]];
3043     }
3044     const SMDS_MeshElement* e;
3045     if ( elemQueue )
3046       for ( int iE = 0; iE < nbElems; ++iE )
3047         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3048           elemQueue->push_back( e );
3049   }
3050 }
3051
3052 //=======================================================================
3053 /*
3054  * Split bi-quadratic elements into linear ones without creation of additional nodes
3055  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
3056  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3057  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3058  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
3059  *   will be split in order to keep the mesh conformal.
3060  *  \param elems - elements to split
3061  */
3062 //=======================================================================
3063
3064 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3065 {
3066   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3067   vector<const SMDS_MeshElement* > splitElems;
3068   list< const SMDS_MeshElement* > elemQueue;
3069   list< const SMDS_MeshElement* >::iterator elemIt;
3070
3071   SMESHDS_Mesh * mesh = GetMeshDS();
3072   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3073   int nbElems, nbNodes;
3074
3075   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3076   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3077   {
3078     elemQueue.clear();
3079     elemQueue.push_back( *elemSetIt );
3080     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3081     {
3082       const SMDS_MeshElement* elem = *elemIt;
3083       switch( elem->GetEntityType() )
3084       {
3085       case SMDSEntity_TriQuad_Hexa: // HEX27
3086       {
3087         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3088         nbElems  = nbNodes = 8;
3089         elemType = & hexaType;
3090
3091         // get nodes for new elements
3092         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
3093                                  { 1,9,20,8,    17,22,26,21 },
3094                                  { 2,10,20,9,   18,23,26,22 },
3095                                  { 3,11,20,10,  19,24,26,23 },
3096                                  { 16,21,26,24, 4,12,25,15  },
3097                                  { 17,22,26,21, 5,13,25,12  },
3098                                  { 18,23,26,22, 6,14,25,13  },
3099                                  { 19,24,26,23, 7,15,25,14  }};
3100         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3101
3102         // add boundary faces to elemQueue
3103         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
3104                                  { 4,5,6,7, 12,13,14,15, 25 },
3105                                  { 0,1,5,4, 8,17,12,16,  21 },
3106                                  { 1,2,6,5, 9,18,13,17,  22 },
3107                                  { 2,3,7,6, 10,19,14,18, 23 },
3108                                  { 3,0,4,7, 11,16,15,19, 24 }};
3109         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3110
3111         // add boundary segments to elemQueue
3112         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3113                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3114                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3115         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3116         break;
3117       }
3118       case SMDSEntity_BiQuad_Triangle: // TRIA7
3119       {
3120         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3121         nbElems = 3;
3122         nbNodes = 4;
3123         elemType = & quadType;
3124
3125         // get nodes for new elements
3126         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3127         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3128
3129         // add boundary segments to elemQueue
3130         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3131         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3132         break;
3133       }
3134       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3135       {
3136         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3137         nbElems = 4;
3138         nbNodes = 4;
3139         elemType = & quadType;
3140
3141         // get nodes for new elements
3142         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3143         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3144
3145         // add boundary segments to elemQueue
3146         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3147         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3148         break;
3149       }
3150       case SMDSEntity_Quad_Edge:
3151       {
3152         if ( elemIt == elemQueue.begin() )
3153           continue; // an elem is in theElems
3154         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3155         nbElems = 2;
3156         nbNodes = 2;
3157         elemType = & segType;
3158
3159         // get nodes for new elements
3160         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3161         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3162         break;
3163       }
3164       default: continue;
3165       } // switch( elem->GetEntityType() )
3166
3167       // Create new elements
3168
3169       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3170
3171       splitElems.clear();
3172
3173       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3174       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3175       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3176       //elemType->SetID( -1 );
3177
3178       for ( int iE = 0; iE < nbElems; ++iE )
3179         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3180
3181
3182       ReplaceElemInGroups( elem, splitElems, mesh );
3183
3184       if ( subMesh )
3185         for ( size_t i = 0; i < splitElems.size(); ++i )
3186           subMesh->AddElement( splitElems[i] );
3187     }
3188   }
3189 }
3190
3191 //=======================================================================
3192 //function : AddToSameGroups
3193 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
3194 //=======================================================================
3195
3196 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3197                                         const SMDS_MeshElement* elemInGroups,
3198                                         SMESHDS_Mesh *          aMesh)
3199 {
3200   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3201   if (!groups.empty()) {
3202     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3203     for ( ; grIt != groups.end(); grIt++ ) {
3204       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3205       if ( group && group->Contains( elemInGroups ))
3206         group->SMDSGroup().Add( elemToAdd );
3207     }
3208   }
3209 }
3210
3211
3212 //=======================================================================
3213 //function : RemoveElemFromGroups
3214 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
3215 //=======================================================================
3216 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3217                                              SMESHDS_Mesh *          aMesh)
3218 {
3219   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3220   if (!groups.empty())
3221   {
3222     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3223     for (; GrIt != groups.end(); GrIt++)
3224     {
3225       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3226       if (!grp || grp->IsEmpty()) continue;
3227       grp->SMDSGroup().Remove(removeelem);
3228     }
3229   }
3230 }
3231
3232 //================================================================================
3233 /*!
3234  * \brief Replace elemToRm by elemToAdd in the all groups
3235  */
3236 //================================================================================
3237
3238 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3239                                             const SMDS_MeshElement* elemToAdd,
3240                                             SMESHDS_Mesh *          aMesh)
3241 {
3242   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3243   if (!groups.empty()) {
3244     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3245     for ( ; grIt != groups.end(); grIt++ ) {
3246       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3247       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3248         group->SMDSGroup().Add( elemToAdd );
3249     }
3250   }
3251 }
3252
3253 //================================================================================
3254 /*!
3255  * \brief Replace elemToRm by elemToAdd in the all groups
3256  */
3257 //================================================================================
3258
3259 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
3260                                             const vector<const SMDS_MeshElement*>& elemToAdd,
3261                                             SMESHDS_Mesh *                         aMesh)
3262 {
3263   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3264   if (!groups.empty())
3265   {
3266     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3267     for ( ; grIt != groups.end(); grIt++ ) {
3268       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3269       if ( group && group->SMDSGroup().Remove( elemToRm ) )
3270         for ( size_t i = 0; i < elemToAdd.size(); ++i )
3271           group->SMDSGroup().Add( elemToAdd[ i ] );
3272     }
3273   }
3274 }
3275
3276 //=======================================================================
3277 //function : QuadToTri
3278 //purpose  : Cut quadrangles into triangles.
3279 //           theCrit is used to select a diagonal to cut
3280 //=======================================================================
3281
3282 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3283                                   const bool         the13Diag)
3284 {
3285   ClearLastCreated();
3286   myLastCreatedElems.reserve( theElems.size() * 2 );
3287
3288   SMESHDS_Mesh *       aMesh = GetMeshDS();
3289   Handle(Geom_Surface) surface;
3290   SMESH_MesherHelper   helper( *GetMesh() );
3291
3292   TIDSortedElemSet::iterator itElem;
3293   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3294   {
3295     const SMDS_MeshElement* elem = *itElem;
3296     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3297       continue;
3298
3299     if ( elem->NbNodes() == 4 ) {
3300       // retrieve element nodes
3301       const SMDS_MeshNode* aNodes [4];
3302       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3303       int i = 0;
3304       while ( itN->more() )
3305         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3306
3307       int aShapeId = FindShape( elem );
3308       const SMDS_MeshElement* newElem1 = 0;
3309       const SMDS_MeshElement* newElem2 = 0;
3310       if ( the13Diag ) {
3311         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3312         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3313       }
3314       else {
3315         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3316         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3317       }
3318       myLastCreatedElems.push_back(newElem1);
3319       myLastCreatedElems.push_back(newElem2);
3320       // put a new triangle on the same shape and add to the same groups
3321       if ( aShapeId )
3322       {
3323         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3324         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3325       }
3326       AddToSameGroups( newElem1, elem, aMesh );
3327       AddToSameGroups( newElem2, elem, aMesh );
3328       aMesh->RemoveElement( elem );
3329     }
3330
3331     // Quadratic quadrangle
3332
3333     else if ( elem->NbNodes() >= 8 )
3334     {
3335       // get surface elem is on
3336       int aShapeId = FindShape( elem );
3337       if ( aShapeId != helper.GetSubShapeID() ) {
3338         surface.Nullify();
3339         TopoDS_Shape shape;
3340         if ( aShapeId > 0 )
3341           shape = aMesh->IndexToShape( aShapeId );
3342         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3343           TopoDS_Face face = TopoDS::Face( shape );
3344           surface = BRep_Tool::Surface( face );
3345           if ( !surface.IsNull() )
3346             helper.SetSubShape( shape );
3347         }
3348       }
3349
3350       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3351       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3352       for ( int i = 0; itN->more(); ++i )
3353         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3354
3355       const SMDS_MeshNode* centrNode = aNodes[8];
3356       if ( centrNode == 0 )
3357       {
3358         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3359                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3360                                            surface.IsNull() );
3361         myLastCreatedNodes.push_back(centrNode);
3362       }
3363
3364       // create a new element
3365       const SMDS_MeshElement* newElem1 = 0;
3366       const SMDS_MeshElement* newElem2 = 0;
3367       if ( the13Diag ) {
3368         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3369                                   aNodes[6], aNodes[7], centrNode );
3370         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3371                                   centrNode, aNodes[4], aNodes[5] );
3372       }
3373       else {
3374         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3375                                   aNodes[7], aNodes[4], centrNode );
3376         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3377                                   centrNode, aNodes[5], aNodes[6] );
3378       }
3379       myLastCreatedElems.push_back(newElem1);
3380       myLastCreatedElems.push_back(newElem2);
3381       // put a new triangle on the same shape and add to the same groups
3382       if ( aShapeId )
3383       {
3384         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3385         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3386       }
3387       AddToSameGroups( newElem1, elem, aMesh );
3388       AddToSameGroups( newElem2, elem, aMesh );
3389       aMesh->RemoveElement( elem );
3390     }
3391   }
3392
3393   return true;
3394 }
3395
3396 //=======================================================================
3397 //function : getAngle
3398 //purpose  :
3399 //=======================================================================
3400
3401 double getAngle(const SMDS_MeshElement * tr1,
3402                 const SMDS_MeshElement * tr2,
3403                 const SMDS_MeshNode *    n1,
3404                 const SMDS_MeshNode *    n2)
3405 {
3406   double angle = 2. * M_PI; // bad angle
3407
3408   // get normals
3409   SMESH::Controls::TSequenceOfXYZ P1, P2;
3410   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3411        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3412     return angle;
3413   gp_Vec N1,N2;
3414   if(!tr1->IsQuadratic())
3415     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3416   else
3417     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3418   if ( N1.SquareMagnitude() <= gp::Resolution() )
3419     return angle;
3420   if(!tr2->IsQuadratic())
3421     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3422   else
3423     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3424   if ( N2.SquareMagnitude() <= gp::Resolution() )
3425     return angle;
3426
3427   // find the first diagonal node n1 in the triangles:
3428   // take in account a diagonal link orientation
3429   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3430   for ( int t = 0; t < 2; t++ ) {
3431     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3432     int i = 0, iDiag = -1;
3433     while ( it->more()) {
3434       const SMDS_MeshElement *n = it->next();
3435       if ( n == n1 || n == n2 ) {
3436         if ( iDiag < 0)
3437           iDiag = i;
3438         else {
3439           if ( i - iDiag == 1 )
3440             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3441           else
3442             nFirst[ t ] = n;
3443           break;
3444         }
3445       }
3446       i++;
3447     }
3448   }
3449   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3450     N2.Reverse();
3451
3452   angle = N1.Angle( N2 );
3453   //SCRUTE( angle );
3454   return angle;
3455 }
3456
3457 // =================================================
3458 // class generating a unique ID for a pair of nodes
3459 // and able to return nodes by that ID
3460 // =================================================
3461 class LinkID_Gen {
3462 public:
3463
3464   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3465     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3466   {}
3467
3468   smIdType GetLinkID (const SMDS_MeshNode * n1,
3469                   const SMDS_MeshNode * n2) const
3470   {
3471     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3472   }
3473
3474   bool GetNodes (const long             theLinkID,
3475                  const SMDS_MeshNode* & theNode1,
3476                  const SMDS_MeshNode* & theNode2) const
3477   {
3478     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3479     if ( !theNode1 ) return false;
3480     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3481     if ( !theNode2 ) return false;
3482     return true;
3483   }
3484
3485 private:
3486   LinkID_Gen();
3487   const SMESHDS_Mesh* myMesh;
3488   long                myMaxID;
3489 };
3490
3491
3492 //=======================================================================
3493 //function : TriToQuad
3494 //purpose  : Fuse neighbour triangles into quadrangles.
3495 //           theCrit is used to select a neighbour to fuse with.
3496 //           theMaxAngle is a max angle between element normals at which
3497 //           fusion is still performed.
3498 //=======================================================================
3499
3500 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3501                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3502                                   const double                         theMaxAngle)
3503 {
3504   ClearLastCreated();
3505   myLastCreatedElems.reserve( theElems.size() / 2 );
3506
3507   if ( !theCrit.get() )
3508     return false;
3509
3510   SMESHDS_Mesh * aMesh = GetMeshDS();
3511
3512   // Prepare data for algo: build
3513   // 1. map of elements with their linkIDs
3514   // 2. map of linkIDs with their elements
3515
3516   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3517   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3518   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3519   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3520
3521   TIDSortedElemSet::iterator itElem;
3522   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3523   {
3524     const SMDS_MeshElement* elem = *itElem;
3525     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3526     bool IsTria = ( elem->NbCornerNodes()==3 );
3527     if (!IsTria) continue;
3528
3529     // retrieve element nodes
3530     const SMDS_MeshNode* aNodes [4];
3531     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3532     int i = 0;
3533     while ( i < 3 )
3534       aNodes[ i++ ] = itN->next();
3535     aNodes[ 3 ] = aNodes[ 0 ];
3536
3537     // fill maps
3538     for ( i = 0; i < 3; i++ ) {
3539       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3540       // check if elements sharing a link can be fused
3541       itLE = mapLi_listEl.find( link );
3542       if ( itLE != mapLi_listEl.end() ) {
3543         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3544           continue;
3545         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3546         //if ( FindShape( elem ) != FindShape( elem2 ))
3547         //  continue; // do not fuse triangles laying on different shapes
3548         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3549           continue; // avoid making badly shaped quads
3550         (*itLE).second.push_back( elem );
3551       }
3552       else {
3553         mapLi_listEl[ link ].push_back( elem );
3554       }
3555       mapEl_setLi [ elem ].insert( link );
3556     }
3557   }
3558   // Clean the maps from the links shared by a sole element, ie
3559   // links to which only one element is bound in mapLi_listEl
3560
3561   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3562     int nbElems = (*itLE).second.size();
3563     if ( nbElems < 2  ) {
3564       const SMDS_MeshElement* elem = (*itLE).second.front();
3565       SMESH_TLink link = (*itLE).first;
3566       mapEl_setLi[ elem ].erase( link );
3567       if ( mapEl_setLi[ elem ].empty() )
3568         mapEl_setLi.erase( elem );
3569     }
3570   }
3571
3572   // Algo: fuse triangles into quadrangles
3573
3574   while ( ! mapEl_setLi.empty() ) {
3575     // Look for the start element:
3576     // the element having the least nb of shared links
3577     const SMDS_MeshElement* startElem = 0;
3578     int minNbLinks = 4;
3579     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3580       int nbLinks = (*itEL).second.size();
3581       if ( nbLinks < minNbLinks ) {
3582         startElem = (*itEL).first;
3583         minNbLinks = nbLinks;
3584         if ( minNbLinks == 1 )
3585           break;
3586       }
3587     }
3588
3589     // search elements to fuse starting from startElem or links of elements
3590     // fused earlyer - startLinks
3591     list< SMESH_TLink > startLinks;
3592     while ( startElem || !startLinks.empty() ) {
3593       while ( !startElem && !startLinks.empty() ) {
3594         // Get an element to start, by a link
3595         SMESH_TLink linkId = startLinks.front();
3596         startLinks.pop_front();
3597         itLE = mapLi_listEl.find( linkId );
3598         if ( itLE != mapLi_listEl.end() ) {
3599           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3600           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3601           for ( ; itE != listElem.end() ; itE++ )
3602             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3603               startElem = (*itE);
3604           mapLi_listEl.erase( itLE );
3605         }
3606       }
3607
3608       if ( startElem ) {
3609         // Get candidates to be fused
3610         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3611         const SMESH_TLink *link12 = 0, *link13 = 0;
3612         startElem = 0;
3613         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3614         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3615         ASSERT( !setLi.empty() );
3616         set< SMESH_TLink >::iterator itLi;
3617         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3618         {
3619           const SMESH_TLink & link = (*itLi);
3620           itLE = mapLi_listEl.find( link );
3621           if ( itLE == mapLi_listEl.end() )
3622             continue;
3623
3624           const SMDS_MeshElement* elem = (*itLE).second.front();
3625           if ( elem == tr1 )
3626             elem = (*itLE).second.back();
3627           mapLi_listEl.erase( itLE );
3628           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3629             continue;
3630           if ( tr2 ) {
3631             tr3 = elem;
3632             link13 = &link;
3633           }
3634           else {
3635             tr2 = elem;
3636             link12 = &link;
3637           }
3638
3639           // add other links of elem to list of links to re-start from
3640           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3641           set< SMESH_TLink >::iterator it;
3642           for ( it = links.begin(); it != links.end(); it++ ) {
3643             const SMESH_TLink& link2 = (*it);
3644             if ( link2 != link )
3645               startLinks.push_back( link2 );
3646           }
3647         }
3648
3649         // Get nodes of possible quadrangles
3650         const SMDS_MeshNode *n12 [4], *n13 [4];
3651         bool Ok12 = false, Ok13 = false;
3652         const SMDS_MeshNode *linkNode1, *linkNode2;
3653         if(tr2) {
3654           linkNode1 = link12->first;
3655           linkNode2 = link12->second;
3656           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3657             Ok12 = true;
3658         }
3659         if(tr3) {
3660           linkNode1 = link13->first;
3661           linkNode2 = link13->second;
3662           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3663             Ok13 = true;
3664         }
3665
3666         // Choose a pair to fuse
3667         if ( Ok12 && Ok13 ) {
3668           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3669           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3670           double aBadRate12 = getBadRate( &quad12, theCrit );
3671           double aBadRate13 = getBadRate( &quad13, theCrit );
3672           if (  aBadRate13 < aBadRate12 )
3673             Ok12 = false;
3674           else
3675             Ok13 = false;
3676         }
3677
3678         // Make quadrangles
3679         // and remove fused elems and remove links from the maps
3680         mapEl_setLi.erase( tr1 );
3681         if ( Ok12 )
3682         {
3683           mapEl_setLi.erase( tr2 );
3684           mapLi_listEl.erase( *link12 );
3685           if ( tr1->NbNodes() == 3 )
3686           {
3687             const SMDS_MeshElement* newElem = 0;
3688             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3689             myLastCreatedElems.push_back(newElem);
3690             AddToSameGroups( newElem, tr1, aMesh );
3691             int aShapeId = tr1->getshapeId();
3692             if ( aShapeId )
3693               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3694             aMesh->RemoveElement( tr1 );
3695             aMesh->RemoveElement( tr2 );
3696           }
3697           else {
3698             vector< const SMDS_MeshNode* > N1;
3699             vector< const SMDS_MeshNode* > N2;
3700             getNodesFromTwoTria(tr1,tr2,N1,N2);
3701             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3702             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3703             // i.e. first nodes from both arrays form a new diagonal
3704             const SMDS_MeshNode* aNodes[8];
3705             aNodes[0] = N1[0];
3706             aNodes[1] = N1[1];
3707             aNodes[2] = N2[0];
3708             aNodes[3] = N2[1];
3709             aNodes[4] = N1[3];
3710             aNodes[5] = N2[5];
3711             aNodes[6] = N2[3];
3712             aNodes[7] = N1[5];
3713             const SMDS_MeshElement* newElem = 0;
3714             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3715               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3716                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3717             else
3718               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3719                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3720             myLastCreatedElems.push_back(newElem);
3721             AddToSameGroups( newElem, tr1, aMesh );
3722             int aShapeId = tr1->getshapeId();
3723             if ( aShapeId )
3724               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3725             aMesh->RemoveElement( tr1 );
3726             aMesh->RemoveElement( tr2 );
3727             // remove middle node (9)
3728             if ( N1[4]->NbInverseElements() == 0 )
3729               aMesh->RemoveNode( N1[4] );
3730             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3731               aMesh->RemoveNode( N1[6] );
3732             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3733               aMesh->RemoveNode( N2[6] );
3734           }
3735         }
3736         else if ( Ok13 )
3737         {
3738           mapEl_setLi.erase( tr3 );
3739           mapLi_listEl.erase( *link13 );
3740           if ( tr1->NbNodes() == 3 ) {
3741             const SMDS_MeshElement* newElem = 0;
3742             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3743             myLastCreatedElems.push_back(newElem);
3744             AddToSameGroups( newElem, tr1, aMesh );
3745             int aShapeId = tr1->getshapeId();
3746             if ( aShapeId )
3747               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3748             aMesh->RemoveElement( tr1 );
3749             aMesh->RemoveElement( tr3 );
3750           }
3751           else {
3752             vector< const SMDS_MeshNode* > N1;
3753             vector< const SMDS_MeshNode* > N2;
3754             getNodesFromTwoTria(tr1,tr3,N1,N2);
3755             // now we receive following N1 and N2 (using numeration as above image)
3756             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3757             // i.e. first nodes from both arrays form a new diagonal
3758             const SMDS_MeshNode* aNodes[8];
3759             aNodes[0] = N1[0];
3760             aNodes[1] = N1[1];
3761             aNodes[2] = N2[0];
3762             aNodes[3] = N2[1];
3763             aNodes[4] = N1[3];
3764             aNodes[5] = N2[5];
3765             aNodes[6] = N2[3];
3766             aNodes[7] = N1[5];
3767             const SMDS_MeshElement* newElem = 0;
3768             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3769               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3770                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3771             else
3772               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3773                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3774             myLastCreatedElems.push_back(newElem);
3775             AddToSameGroups( newElem, tr1, aMesh );
3776             int aShapeId = tr1->getshapeId();
3777             if ( aShapeId )
3778               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3779             aMesh->RemoveElement( tr1 );
3780             aMesh->RemoveElement( tr3 );
3781             // remove middle node (9)
3782             if ( N1[4]->NbInverseElements() == 0 )
3783               aMesh->RemoveNode( N1[4] );
3784             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3785               aMesh->RemoveNode( N1[6] );
3786             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3787               aMesh->RemoveNode( N2[6] );
3788           }
3789         }
3790
3791         // Next element to fuse: the rejected one
3792         if ( tr3 )
3793           startElem = Ok12 ? tr3 : tr2;
3794
3795       } // if ( startElem )
3796     } // while ( startElem || !startLinks.empty() )
3797   } // while ( ! mapEl_setLi.empty() )
3798
3799   return true;
3800 }
3801
3802 //================================================================================
3803 /*!
3804  * \brief Return nodes linked to the given one
3805  * \param theNode - the node
3806  * \param linkedNodes - the found nodes
3807  * \param type - the type of elements to check
3808  *
3809  * Medium nodes are ignored
3810  */
3811 //================================================================================
3812
3813 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3814                                        TIDSortedElemSet &   linkedNodes,
3815                                        SMDSAbs_ElementType  type )
3816 {
3817   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3818   while ( elemIt->more() )
3819   {
3820     const SMDS_MeshElement* elem = elemIt->next();
3821     if(elem->GetType() == SMDSAbs_0DElement)
3822       continue;
3823
3824     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3825     if ( elem->GetType() == SMDSAbs_Volume )
3826     {
3827       SMDS_VolumeTool vol( elem );
3828       while ( nodeIt->more() ) {
3829         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3830         if ( theNode != n && vol.IsLinked( theNode, n ))
3831           linkedNodes.insert( n );
3832       }
3833     }
3834     else
3835     {
3836       for ( int i = 0; nodeIt->more(); ++i ) {
3837         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3838         if ( n == theNode ) {
3839           int iBefore = i - 1;
3840           int iAfter  = i + 1;
3841           if ( elem->IsQuadratic() ) {
3842             int nb = elem->NbNodes() / 2;
3843             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3844             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3845           }
3846           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3847           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3848         }
3849       }
3850     }
3851   }
3852 }
3853
3854 //=======================================================================
3855 //function : averageBySurface
3856 //purpose  : Auxiliar function to treat properly nodes in periodic faces in the laplacian smoother
3857 //=======================================================================
3858 void averageBySurface( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, 
3859                         TIDSortedElemSet& nodeSet, map< const SMDS_MeshNode*, gp_XY* >& theUVMap, double * coord )
3860 {
3861   if ( theSurface.IsNull() ) 
3862   {
3863     TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3864     for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) 
3865     {
3866       const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3867       coord[0] += node->X();
3868       coord[1] += node->Y();
3869       coord[2] += node->Z();
3870     }
3871   }
3872   else
3873   {
3874     Standard_Real Umin,Umax,Vmin,Vmax;
3875     theSurface->Bounds( Umin, Umax, Vmin, Vmax );
3876     ASSERT( theUVMap.find( refNode ) != theUVMap.end() );
3877     gp_XY* nodeUV = theUVMap[ refNode ];
3878     Standard_Real uref = nodeUV->X();
3879     Standard_Real vref = nodeUV->Y();
3880
3881     TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3882     for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) 
3883     {
3884       const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3885       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3886       gp_XY* uv = theUVMap[ node ];    
3887
3888       if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )  
3889       {          
3890         Standard_Real u          = uv->X();
3891         Standard_Real v          = uv->Y();                      
3892         Standard_Real uCorrected = u;
3893         Standard_Real vCorrected = v;
3894         bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3895         bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3896
3897         if( isUTobeCorrected  )
3898           uCorrected = uref > u ? Umax + std::fabs(Umin - u) : Umin - std::fabs(Umax - u);
3899
3900         if( isVTobeCorrected )
3901           vCorrected = vref > v ? Vmax + std::fabs(Vmin - v) : Vmin - std::fabs(Vmax - v);
3902         
3903         coord[0] += uCorrected;
3904         coord[1] += vCorrected;
3905
3906       }
3907       else
3908       {
3909         coord[0] += uv->X();
3910         coord[1] += uv->Y();
3911       }
3912     }   
3913   }
3914 }
3915
3916 //=======================================================================
3917 //function : laplacianSmooth
3918 //purpose  : pulls theNode toward the center of surrounding nodes directly
3919 //           connected to that node along an element edge
3920 //=======================================================================
3921
3922 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3923                      const Handle(Geom_Surface)&          theSurface,
3924                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3925 {
3926   // find surrounding nodes
3927
3928   TIDSortedElemSet nodeSet;
3929   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3930
3931   // compute new coodrs
3932   double coord[] = { 0., 0., 0. };  
3933
3934   averageBySurface( theSurface, theNode, nodeSet, theUVMap, coord );
3935
3936   int nbNodes = nodeSet.size();
3937   if ( !nbNodes )
3938     return;
3939
3940   coord[0] /= nbNodes;
3941   coord[1] /= nbNodes;
3942
3943   if ( !theSurface.IsNull() ) {
3944     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3945     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3946     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3947     coord[0] = p3d.X();
3948     coord[1] = p3d.Y();
3949     coord[2] = p3d.Z();    
3950   }
3951   else
3952     coord[2] /= nbNodes;
3953
3954   // move node
3955
3956   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3957 }
3958
3959 //=======================================================================
3960 //function : correctTheValue
3961 //purpose  : Given a boundaries of parametric space determine if the node coordinate (u,v) need correction 
3962 //            based on the reference coordinate (uref,vref)
3963 //=======================================================================
3964 void correctTheValue( Standard_Real Umax, Standard_Real Umin, Standard_Real Vmax, Standard_Real Vmin, 
3965                         Standard_Real uref, Standard_Real vref, Standard_Real &u, Standard_Real &v  )
3966 {
3967   bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3968   bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3969   if ( isUTobeCorrected )
3970     u = std::fabs(u-Umin) < 1e-7 ? Umax : Umin;            
3971   if ( isVTobeCorrected )
3972     v = std::fabs(v-Vmin) < 1e-7 ? Vmax : Vmin;
3973 }
3974
3975 //=======================================================================
3976 //function : averageByElement
3977 //purpose  : Auxiliar function to treat properly nodes in periodic faces in the centroidal smoother
3978 //=======================================================================
3979 void averageByElement( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, const SMDS_MeshElement* elem,
3980                         map< const SMDS_MeshNode*, gp_XY* >& theUVMap, SMESH::Controls::TSequenceOfXYZ& aNodePoints, 
3981                         gp_XYZ& elemCenter )
3982 {
3983   int nn = elem->NbNodes();
3984   if(elem->IsQuadratic()) nn = nn/2;
3985   int i=0;
3986   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3987   Standard_Real Umin,Umax,Vmin,Vmax;
3988   while ( i<nn ) 
3989   {
3990     const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3991     i++;
3992     gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3993     aNodePoints.push_back( aP );
3994     if ( !theSurface.IsNull() ) // smooth in 2D
3995     { 
3996       ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3997       gp_XY* uv = theUVMap[ aNode ];
3998
3999       if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )  
4000       {  
4001         theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4002         Standard_Real u          = uv->X();
4003         Standard_Real v          = uv->Y();   
4004         bool isSingularPoint     = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4005         if ( !isSingularPoint )
4006         {
4007           aP.SetCoord( uv->X(), uv->Y(), 0. );
4008         }
4009         else
4010         {
4011           gp_XY* refPoint = theUVMap[ refNode ];
4012           Standard_Real uref = refPoint->X();
4013           Standard_Real vref = refPoint->Y();
4014           correctTheValue( Umax, Umin, Vmax, Vmin, uref, vref, u, v ); 
4015           aP.SetCoord( u, v, 0. );
4016         }        
4017       }
4018       else
4019         aP.SetCoord( uv->X(), uv->Y(), 0. );
4020     }    
4021     elemCenter += aP;   
4022   }
4023 }
4024
4025 //=======================================================================
4026 //function : centroidalSmooth
4027 //purpose  : pulls theNode toward the element-area-weighted centroid of the
4028 //           surrounding elements
4029 //=======================================================================
4030
4031 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
4032                       const Handle(Geom_Surface)&          theSurface,
4033                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
4034 {
4035   gp_XYZ aNewXYZ(0.,0.,0.);
4036   SMESH::Controls::Area anAreaFunc;
4037   double totalArea = 0.;
4038   int nbElems = 0;
4039   // compute new XYZ
4040   bool notToMoveNode = false;
4041   // Do not correct singular nodes
4042   if ( !theSurface.IsNull() && (theSurface->IsUPeriodic() || theSurface->IsVPeriodic()) )
4043   { 
4044     Standard_Real Umin,Umax,Vmin,Vmax;
4045     theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4046     gp_XY* uv = theUVMap[ theNode ];
4047     Standard_Real u = uv->X();
4048     Standard_Real v = uv->Y();   
4049     notToMoveNode = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4050   }
4051   
4052   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
4053   while ( elemIt->more() && !notToMoveNode )
4054   {
4055     const SMDS_MeshElement* elem = elemIt->next();
4056     nbElems++;
4057
4058     gp_XYZ elemCenter(0.,0.,0.);
4059     SMESH::Controls::TSequenceOfXYZ aNodePoints;
4060     int nn = elem->NbNodes();
4061     if(elem->IsQuadratic()) nn = nn/2;
4062     averageByElement( theSurface, theNode, elem, theUVMap, aNodePoints, elemCenter );
4063
4064     double elemArea = anAreaFunc.GetValue( aNodePoints );
4065     totalArea += elemArea;
4066     elemCenter /= nn;
4067     aNewXYZ += elemCenter * elemArea;
4068   }
4069   aNewXYZ /= totalArea;
4070   
4071   if ( !theSurface.IsNull() && !notToMoveNode ) {
4072     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
4073     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
4074   }
4075
4076   // move node
4077   if ( !notToMoveNode )
4078     const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
4079 }
4080
4081 //=======================================================================
4082 //function : getClosestUV
4083 //purpose  : return UV of closest projection
4084 //=======================================================================
4085
4086 static bool getClosestUV (Extrema_GenExtPS& projector,
4087                           const gp_Pnt&     point,
4088                           gp_XY &           result)
4089 {
4090   projector.Perform( point );
4091   if ( projector.IsDone() ) {
4092     double u = 0, v = 0, minVal = DBL_MAX;
4093     for ( int i = projector.NbExt(); i > 0; i-- )
4094       if ( projector.SquareDistance( i ) < minVal ) {
4095         minVal = projector.SquareDistance( i );
4096         projector.Point( i ).Parameter( u, v );
4097       }
4098     result.SetCoord( u, v );
4099     return true;
4100   }
4101   return false;
4102 }
4103
4104 //=======================================================================
4105 //function : Smooth
4106 //purpose  : Smooth theElements during theNbIterations or until a worst
4107 //           element has aspect ratio <= theTgtAspectRatio.
4108 //           Aspect Ratio varies in range [1.0, inf].
4109 //           If theElements is empty, the whole mesh is smoothed.
4110 //           theFixedNodes contains additionally fixed nodes. Nodes built
4111 //           on edges and boundary nodes are always fixed.
4112 //=======================================================================
4113
4114 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
4115                                set<const SMDS_MeshNode*> & theFixedNodes,
4116                                const SmoothMethod          theSmoothMethod,
4117                                const int                   theNbIterations,
4118                                double                      theTgtAspectRatio,
4119                                const bool                  the2D)
4120 {
4121   ClearLastCreated();
4122
4123   if ( theTgtAspectRatio < 1.0 )
4124     theTgtAspectRatio = 1.0;
4125
4126   const double disttol = 1.e-16;
4127
4128   SMESH::Controls::AspectRatio aQualityFunc;
4129
4130   SMESHDS_Mesh* aMesh = GetMeshDS();
4131
4132   if ( theElems.empty() ) {
4133     // add all faces to theElems
4134     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4135     while ( fIt->more() ) {
4136       const SMDS_MeshElement* face = fIt->next();
4137       theElems.insert( theElems.end(), face );
4138     }
4139   }
4140   // get all face ids theElems are on
4141   set< int > faceIdSet;
4142   TIDSortedElemSet::iterator itElem;
4143   if ( the2D )
4144     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4145       int fId = FindShape( *itElem );
4146       // check that corresponding submesh exists and a shape is face
4147       if (fId &&
4148           faceIdSet.find( fId ) == faceIdSet.end() &&
4149           aMesh->MeshElements( fId )) {
4150         TopoDS_Shape F = aMesh->IndexToShape( fId );
4151         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4152           faceIdSet.insert( fId );
4153       }
4154     }
4155   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4156
4157   // ===============================================
4158   // smooth elements on each TopoDS_Face separately
4159   // ===============================================
4160
4161   SMESH_MesherHelper helper( *GetMesh() );
4162
4163   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4164   for ( ; fId != faceIdSet.rend(); ++fId )
4165   {
4166     // get face surface and submesh
4167     Handle(Geom_Surface) surface;
4168     SMESHDS_SubMesh* faceSubMesh = 0;
4169     TopoDS_Face face;
4170     double fToler2 = 0;
4171     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4172     bool isUPeriodic = false, isVPeriodic = false;
4173     if ( *fId )
4174     {
4175       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4176       surface = BRep_Tool::Surface( face );
4177       faceSubMesh = aMesh->MeshElements( *fId );
4178       fToler2 = BRep_Tool::Tolerance( face );
4179       fToler2 *= fToler2 * 10.;
4180       isUPeriodic = surface->IsUPeriodic();
4181       // if ( isUPeriodic )
4182       //   surface->UPeriod();
4183       isVPeriodic = surface->IsVPeriodic();
4184       // if ( isVPeriodic )
4185       //   surface->VPeriod();
4186       surface->Bounds( u1, u2, v1, v2 );
4187       helper.SetSubShape( face );
4188     }
4189     // ---------------------------------------------------------
4190     // for elements on a face, find movable and fixed nodes and
4191     // compute UV for them
4192     // ---------------------------------------------------------
4193     bool checkBoundaryNodes = false;
4194     bool isQuadratic = false;
4195     set<const SMDS_MeshNode*> setMovableNodes;
4196     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4197     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4198     list< const SMDS_MeshElement* > elemsOnFace;
4199
4200     Extrema_GenExtPS projector;
4201     GeomAdaptor_Surface surfAdaptor;
4202     if ( !surface.IsNull() ) {
4203       surfAdaptor.Load( surface );
4204       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4205     }
4206     int nbElemOnFace = 0;
4207     itElem = theElems.begin();
4208     // loop on not yet smoothed elements: look for elems on a face
4209     while ( itElem != theElems.end() )
4210     {
4211       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4212         break; // all elements found
4213
4214       const SMDS_MeshElement* elem = *itElem;
4215       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4216            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4217         ++itElem;
4218         continue;
4219       }
4220       elemsOnFace.push_back( elem );
4221       theElems.erase( itElem++ );
4222       nbElemOnFace++;
4223
4224       if ( !isQuadratic )
4225         isQuadratic = elem->IsQuadratic();
4226
4227       // get movable nodes of elem
4228       const SMDS_MeshNode* node;
4229       SMDS_TypeOfPosition posType;
4230       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4231       int nn = 0, nbn =  elem->NbNodes();
4232       if(elem->IsQuadratic())
4233         nbn = nbn/2;
4234       while ( nn++ < nbn ) {
4235         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4236         const SMDS_PositionPtr& pos = node->GetPosition();
4237         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4238         if (posType != SMDS_TOP_EDGE &&
4239             posType != SMDS_TOP_VERTEX &&
4240             theFixedNodes.find( node ) == theFixedNodes.end())
4241         {
4242           // check if all faces around the node are on faceSubMesh
4243           // because a node on edge may be bound to face
4244           bool all = true;
4245           if ( faceSubMesh ) {
4246             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4247             while ( eIt->more() && all ) {
4248               const SMDS_MeshElement* e = eIt->next();
4249               all = faceSubMesh->Contains( e );
4250             }
4251           }
4252           if ( all )
4253             setMovableNodes.insert( node );
4254           else
4255             checkBoundaryNodes = true;
4256         }
4257         if ( posType == SMDS_TOP_3DSPACE )
4258           checkBoundaryNodes = true;
4259       }
4260
4261       if ( surface.IsNull() )
4262         continue;
4263
4264       // get nodes to check UV
4265       list< const SMDS_MeshNode* > uvCheckNodes;
4266       const SMDS_MeshNode* nodeInFace = 0;
4267       itN = elem->nodesIterator();
4268       nn = 0; nbn =  elem->NbNodes();
4269       if(elem->IsQuadratic())
4270         nbn = nbn/2;
4271       while ( nn++ < nbn ) {
4272         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4273         if ( node->GetPosition()->GetDim() == 2 )
4274           nodeInFace = node;
4275         if ( uvMap.find( node ) == uvMap.end() )
4276           uvCheckNodes.push_back( node );
4277         // add nodes of elems sharing node
4278         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4279         //         while ( eIt->more() ) {
4280         //           const SMDS_MeshElement* e = eIt->next();
4281         //           if ( e != elem ) {
4282         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4283         //             while ( nIt->more() ) {
4284         //               const SMDS_MeshNode* n =
4285         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4286         //               if ( uvMap.find( n ) == uvMap.end() )
4287         //                 uvCheckNodes.push_back( n );
4288         //             }
4289         //           }
4290         //         }
4291       }
4292       // check UV on face
4293       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4294       for ( ; n != uvCheckNodes.end(); ++n ) {
4295         node = *n;
4296         gp_XY uv( 0, 0 );
4297         const SMDS_PositionPtr& pos = node->GetPosition();
4298         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4299         // get existing UV
4300         if ( pos )
4301         {
4302           bool toCheck = true;
4303           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4304         }
4305         // compute not existing UV
4306         bool project = ( posType == SMDS_TOP_3DSPACE );
4307         // double dist1 = DBL_MAX, dist2 = 0;
4308         // if ( posType != SMDS_TOP_3DSPACE ) {
4309         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4310         //   project = dist1 > fToler2;
4311         // }
4312         if ( project ) { // compute new UV
4313           gp_XY newUV;
4314           gp_Pnt pNode = SMESH_NodeXYZ( node );
4315           if ( !getClosestUV( projector, pNode, newUV )) {
4316             MESSAGE("Node Projection Failed " << node);
4317           }
4318           else {
4319             if ( isUPeriodic )
4320               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4321             if ( isVPeriodic )
4322               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4323             // check new UV
4324             // if ( posType != SMDS_TOP_3DSPACE )
4325             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4326             // if ( dist2 < dist1 )
4327             uv = newUV;
4328           }
4329         }
4330         // store UV in the map
4331         listUV.push_back( uv );
4332         uvMap.insert( make_pair( node, &listUV.back() ));
4333       }
4334     } // loop on not yet smoothed elements
4335
4336     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4337       checkBoundaryNodes = true;
4338
4339     // fix nodes on mesh boundary
4340
4341     if ( checkBoundaryNodes ) {
4342       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4343       map< SMESH_TLink, int >::iterator link_nb;
4344       // put all elements links to linkNbMap
4345       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4346       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4347         const SMDS_MeshElement* elem = (*elemIt);
4348         int nbn =  elem->NbCornerNodes();
4349         // loop on elem links: insert them in linkNbMap
4350         for ( int iN = 0; iN < nbn; ++iN ) {
4351           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4352           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4353           SMESH_TLink link( n1, n2 );
4354           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4355           link_nb->second++;
4356         }
4357       }
4358       // remove nodes that are in links encountered only once from setMovableNodes
4359       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4360         if ( link_nb->second == 1 ) {
4361           setMovableNodes.erase( link_nb->first.node1() );
4362           setMovableNodes.erase( link_nb->first.node2() );
4363         }
4364       }
4365     }
4366
4367     // -----------------------------------------------------
4368     // for nodes on seam edge, compute one more UV ( uvMap2 );
4369     // find movable nodes linked to nodes on seam and which
4370     // are to be smoothed using the second UV ( uvMap2 )
4371     // -----------------------------------------------------
4372
4373     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4374     if ( !surface.IsNull() ) {
4375       TopExp_Explorer eExp( face, TopAbs_EDGE );
4376       for ( ; eExp.More(); eExp.Next() ) {
4377         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4378         if ( !BRep_Tool::IsClosed( edge, face ))
4379           continue;
4380         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4381         if ( !sm )
4382           continue;
4383         // find out which parameter varies for a node on seam
4384         double f,l;
4385         gp_Pnt2d uv1, uv2;
4386         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4387         if ( pcurve.IsNull() ) continue;
4388         uv1 = pcurve->Value( f );
4389         edge.Reverse();
4390         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4391         if ( pcurve.IsNull() ) continue;
4392         uv2 = pcurve->Value( f );
4393         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4394         // assure uv1 < uv2
4395         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4396           std::swap( uv1, uv2 );
4397         // get nodes on seam and its vertices
4398         list< const SMDS_MeshNode* > seamNodes;
4399         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4400         while ( nSeamIt->more() ) {
4401           const SMDS_MeshNode* node = nSeamIt->next();
4402           if ( !isQuadratic || !IsMedium( node ))
4403             seamNodes.push_back( node );
4404         }
4405         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4406         for ( ; vExp.More(); vExp.Next() ) {
4407           sm = aMesh->MeshElements( vExp.Current() );
4408           if ( sm ) {
4409             nSeamIt = sm->GetNodes();
4410             while ( nSeamIt->more() )
4411               seamNodes.push_back( nSeamIt->next() );
4412           }
4413         }
4414         // loop on nodes on seam
4415         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4416         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4417           const SMDS_MeshNode* nSeam = *noSeIt;
4418           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4419           if ( n_uv == uvMap.end() )
4420             continue;
4421           // set the first UV
4422           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4423           // set the second UV
4424           listUV.push_back( *n_uv->second );
4425           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4426           if ( uvMap2.empty() )
4427             uvMap2 = uvMap; // copy the uvMap contents
4428           uvMap2[ nSeam ] = &listUV.back();
4429
4430           // collect movable nodes linked to ones on seam in nodesNearSeam
4431           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4432           while ( eIt->more() ) {
4433             const SMDS_MeshElement* e = eIt->next();
4434             int nbUseMap1 = 0, nbUseMap2 = 0;
4435             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4436             int nn = 0, nbn =  e->NbNodes();
4437             if(e->IsQuadratic()) nbn = nbn/2;
4438             while ( nn++ < nbn )
4439             {
4440               const SMDS_MeshNode* n =
4441                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4442               if (n == nSeam ||
4443                   setMovableNodes.find( n ) == setMovableNodes.end() )
4444                 continue;
4445               // add only nodes being closer to uv2 than to uv1
4446               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4447               //              0.5 * ( n->Y() + nSeam->Y() ),
4448               //              0.5 * ( n->Z() + nSeam->Z() ));
4449               // gp_XY uv;
4450               // getClosestUV( projector, pMid, uv );
4451               double x = uvMap[ n ]->Coord( iPar );
4452               if ( Abs( uv1.Coord( iPar ) - x ) >
4453                    Abs( uv2.Coord( iPar ) - x )) {
4454                 nodesNearSeam.insert( n );
4455                 nbUseMap2++;
4456               }
4457               else
4458                 nbUseMap1++;
4459             }
4460             // for centroidalSmooth all element nodes must
4461             // be on one side of a seam
4462             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4463               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4464               nn = 0;
4465               while ( nn++ < nbn ) {
4466                 const SMDS_MeshNode* n =
4467                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4468                 setMovableNodes.erase( n );
4469               }
4470             }
4471           }
4472         } // loop on nodes on seam
4473       } // loop on edge of a face
4474     } // if ( !face.IsNull() )
4475
4476     if ( setMovableNodes.empty() ) {
4477       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4478       continue; // goto next face
4479     }
4480
4481     // -------------
4482     // SMOOTHING //
4483     // -------------
4484
4485     int it = -1;
4486     double maxRatio = -1., maxDisplacement = -1.;
4487     set<const SMDS_MeshNode*>::iterator nodeToMove;
4488     for ( it = 0; it < theNbIterations; it++ ) {
4489       maxDisplacement = 0.;
4490       nodeToMove = setMovableNodes.begin();
4491       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4492         const SMDS_MeshNode* node = (*nodeToMove);
4493         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4494
4495         // smooth
4496         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4497         if ( theSmoothMethod == LAPLACIAN )
4498           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4499         else
4500           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4501
4502         // node displacement
4503         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4504         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4505         if ( aDispl > maxDisplacement )
4506           maxDisplacement = aDispl;
4507       }
4508       // no node movement => exit
4509       //if ( maxDisplacement < 1.e-16 ) {
4510       if ( maxDisplacement < disttol ) {
4511         MESSAGE("-- no node movement --");
4512         break;
4513       }
4514
4515       // check elements quality
4516       maxRatio  = 0;
4517       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4518       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4519         const SMDS_MeshElement* elem = (*elemIt);
4520         if ( !elem || elem->GetType() != SMDSAbs_Face )
4521           continue;
4522         SMESH::Controls::TSequenceOfXYZ aPoints;
4523         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4524           double aValue = aQualityFunc.GetValue( aPoints );
4525           if ( aValue > maxRatio )
4526             maxRatio = aValue;
4527         }
4528       }
4529       if ( maxRatio <= theTgtAspectRatio ) {
4530         //MESSAGE("-- quality achieved --");
4531         break;
4532       }
4533       if (it+1 == theNbIterations) {
4534         //MESSAGE("-- Iteration limit exceeded --");
4535       }
4536     } // smoothing iterations
4537
4538     // MESSAGE(" Face id: " << *fId <<
4539     //         " Nb iterstions: " << it <<
4540     //         " Displacement: " << maxDisplacement <<
4541     //         " Aspect Ratio " << maxRatio);
4542
4543     // ---------------------------------------
4544     // new nodes positions are computed,
4545     // record movement in DS and set new UV
4546     // ---------------------------------------
4547     nodeToMove = setMovableNodes.begin();
4548     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4549       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4550       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4551       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4552       if ( node_uv != uvMap.end() ) {
4553         gp_XY* uv = node_uv->second;
4554         node->SetPosition
4555           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4556       }
4557     }
4558
4559     // move medium nodes of quadratic elements
4560     if ( isQuadratic )
4561     {
4562       vector<const SMDS_MeshNode*> nodes;
4563       bool checkUV;
4564       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4565       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4566       {
4567         const SMDS_MeshElement* QF = *elemIt;
4568         if ( QF->IsQuadratic() )
4569         {
4570           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4571                         SMDS_MeshElement::iterator() );
4572           nodes.push_back( nodes[0] );
4573           gp_Pnt xyz;
4574           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4575           {
4576             if ( !surface.IsNull() )
4577             {
4578               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4579               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4580               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4581               xyz = surface->Value( uv.X(), uv.Y() );
4582             }
4583             else {
4584               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4585             }
4586             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4587               // we have to move a medium node
4588               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4589           }
4590         }
4591       }
4592     }
4593
4594   } // loop on face ids
4595
4596 }
4597
4598 namespace
4599 {
4600   //=======================================================================
4601   //function : isReverse
4602   //purpose  : Return true if normal of prevNodes is not co-directied with
4603   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4604   //           iNotSame is where prevNodes and nextNodes are different.
4605   //           If result is true then future volume orientation is OK
4606   //=======================================================================
4607
4608   bool isReverse(const SMDS_MeshElement*             face,
4609                  const vector<const SMDS_MeshNode*>& prevNodes,
4610                  const vector<const SMDS_MeshNode*>& nextNodes,
4611                  const int                           iNotSame)
4612   {
4613
4614     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4615     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4616     gp_XYZ extrDir( pN - pP ), faceNorm;
4617     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4618
4619     return faceNorm * extrDir < 0.0;
4620   }
4621
4622   //================================================================================
4623   /*!
4624    * \brief Assure that theElemSets[0] holds elements, not nodes
4625    */
4626   //================================================================================
4627
4628   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4629   {
4630     if ( !theElemSets[0].empty() &&
4631          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4632     {
4633       std::swap( theElemSets[0], theElemSets[1] );
4634     }
4635     else if ( !theElemSets[1].empty() &&
4636               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4637     {
4638       std::swap( theElemSets[0], theElemSets[1] );
4639     }
4640   }
4641 }
4642
4643 //=======================================================================
4644 /*!
4645  * \brief Create elements by sweeping an element
4646  * \param elem - element to sweep
4647  * \param newNodesItVec - nodes generated from each node of the element
4648  * \param newElems - generated elements
4649  * \param nbSteps - number of sweeping steps
4650  * \param srcElements - to append elem for each generated element
4651  */
4652 //=======================================================================
4653
4654 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4655                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4656                                     list<const SMDS_MeshElement*>&        newElems,
4657                                     const size_t                          nbSteps,
4658                                     SMESH_SequenceOfElemPtr&              srcElements)
4659 {
4660   SMESHDS_Mesh* aMesh = GetMeshDS();
4661
4662   const int           nbNodes = elem->NbNodes();
4663   const int         nbCorners = elem->NbCornerNodes();
4664   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4665                                                           polyhedron creation !!! */
4666   // Loop on elem nodes:
4667   // find new nodes and detect same nodes indices
4668   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4669   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4670   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4671   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4672
4673   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4674   vector<int> sames(nbNodes);
4675   vector<bool> isSingleNode(nbNodes);
4676
4677   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4678     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4679     const SMDS_MeshNode*                         node = nnIt->first;
4680     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4681     if ( listNewNodes.empty() )
4682       return;
4683
4684     itNN   [ iNode ] = listNewNodes.begin();
4685     prevNod[ iNode ] = node;
4686     nextNod[ iNode ] = listNewNodes.front();
4687
4688     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4689                                                              corner node of linear */
4690     if ( prevNod[ iNode ] != nextNod [ iNode ])
4691       nbDouble += !isSingleNode[iNode];
4692
4693     if( iNode < nbCorners ) { // check corners only
4694       if ( prevNod[ iNode ] == nextNod [ iNode ])
4695         sames[nbSame++] = iNode;
4696       else
4697         iNotSameNode = iNode;
4698     }
4699   }
4700
4701   if ( nbSame == nbNodes || nbSame > 2) {
4702     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4703     return;
4704   }
4705
4706   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4707   {
4708     // fix nodes order to have bottom normal external
4709     if ( baseType == SMDSEntity_Polygon )
4710     {
4711       std::reverse( itNN.begin(), itNN.end() );
4712       std::reverse( prevNod.begin(), prevNod.end() );
4713       std::reverse( midlNod.begin(), midlNod.end() );
4714       std::reverse( nextNod.begin(), nextNod.end() );
4715       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4716     }
4717     else
4718     {
4719       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4720       SMDS_MeshCell::applyInterlace( ind, itNN );
4721       SMDS_MeshCell::applyInterlace( ind, prevNod );
4722       SMDS_MeshCell::applyInterlace( ind, nextNod );
4723       SMDS_MeshCell::applyInterlace( ind, midlNod );
4724       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4725       if ( nbSame > 0 )
4726       {
4727         sames[nbSame] = iNotSameNode;
4728         for ( int j = 0; j <= nbSame; ++j )
4729           for ( size_t i = 0; i < ind.size(); ++i )
4730             if ( ind[i] == sames[j] )
4731             {
4732               sames[j] = i;
4733               break;
4734             }
4735         iNotSameNode = sames[nbSame];
4736       }
4737     }
4738   }
4739   else if ( elem->GetType() == SMDSAbs_Edge )
4740   {
4741     // orient a new face same as adjacent one
4742     int i1, i2;
4743     const SMDS_MeshElement* e;
4744     TIDSortedElemSet dummy;
4745     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4746         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4747         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4748     {
4749       // there is an adjacent face, check order of nodes in it
4750       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4751       if ( sameOrder )
4752       {
4753         std::swap( itNN[0],    itNN[1] );
4754         std::swap( prevNod[0], prevNod[1] );
4755         std::swap( nextNod[0], nextNod[1] );
4756         std::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4757         if ( nbSame > 0 )
4758           sames[0] = 1 - sames[0];
4759         iNotSameNode = 1 - iNotSameNode;
4760       }
4761     }
4762   }
4763
4764   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4765   if ( nbSame > 0 ) {
4766     iSameNode    = sames[ nbSame-1 ];
4767     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4768     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4769     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4770   }
4771
4772   if ( baseType == SMDSEntity_Polygon )
4773   {
4774     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4775     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4776   }
4777   else if ( baseType == SMDSEntity_Quad_Polygon )
4778   {
4779     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4780     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4781   }
4782
4783   // make new elements
4784   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4785   {
4786     // get next nodes
4787     for ( iNode = 0; iNode < nbNodes; iNode++ )
4788     {
4789       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4790       nextNod[ iNode ] = *itNN[ iNode ]++;
4791     }
4792
4793     SMDS_MeshElement* aNewElem = 0;
4794     /*if(!elem->IsPoly())*/ {
4795       switch ( baseType ) {
4796       case SMDSEntity_0D:
4797       case SMDSEntity_Node: { // sweep NODE
4798         if ( nbSame == 0 ) {
4799           if ( isSingleNode[0] )
4800             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4801           else
4802             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4803         }
4804         else
4805           return;
4806         break;
4807       }
4808       case SMDSEntity_Edge: { // sweep EDGE
4809         if ( nbDouble == 0 )
4810         {
4811           if ( nbSame == 0 ) // ---> quadrangle
4812             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4813                                       nextNod[ 1 ], nextNod[ 0 ] );
4814           else               // ---> triangle
4815             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4816                                       nextNod[ iNotSameNode ] );
4817         }
4818         else                 // ---> polygon
4819         {
4820           vector<const SMDS_MeshNode*> poly_nodes;
4821           poly_nodes.push_back( prevNod[0] );
4822           poly_nodes.push_back( prevNod[1] );
4823           if ( prevNod[1] != nextNod[1] )
4824           {
4825             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4826             poly_nodes.push_back( nextNod[1] );
4827           }
4828           if ( prevNod[0] != nextNod[0] )
4829           {
4830             poly_nodes.push_back( nextNod[0] );
4831             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4832           }
4833           switch ( poly_nodes.size() ) {
4834           case 3:
4835             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4836             break;
4837           case 4:
4838             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4839                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4840             break;
4841           default:
4842             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4843           }
4844         }
4845         break;
4846       }
4847       case SMDSEntity_Triangle: // TRIANGLE --->
4848       {
4849         if ( nbDouble > 0 ) break;
4850         if ( nbSame == 0 )       // ---> pentahedron
4851           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4852                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4853
4854         else if ( nbSame == 1 )  // ---> pyramid
4855           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4856                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4857                                        nextNod[ iSameNode ]);
4858
4859         else // 2 same nodes:       ---> tetrahedron
4860           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4861                                        nextNod[ iNotSameNode ]);
4862         break;
4863       }
4864       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4865       {
4866         if ( nbSame == 2 )
4867           return;
4868         if ( nbDouble+nbSame == 2 )
4869         {
4870           if(nbSame==0) {      // ---> quadratic quadrangle
4871             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4872                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4873           }
4874           else { //(nbSame==1) // ---> quadratic triangle
4875             if(sames[0]==2) {
4876               return; // medium node on axis
4877             }
4878             else if(sames[0]==0)
4879               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4880                                         prevNod[2], midlNod[1], nextNod[2] );
4881             else // sames[0]==1
4882               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4883                                         prevNod[2], nextNod[2], midlNod[0]);
4884           }
4885         }
4886         else if ( nbDouble == 3 )
4887         {
4888           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4889             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4890                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4891           }
4892         }
4893         else
4894           return;
4895         break;
4896       }
4897       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4898         if ( nbDouble > 0 ) break;
4899
4900         if ( nbSame == 0 )       // ---> hexahedron
4901           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4902                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4903
4904         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4905           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4906                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4907                                        nextNod[ iSameNode ]);
4908           newElems.push_back( aNewElem );
4909           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4910                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4911                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4912         }
4913         else if ( nbSame == 2 ) { // ---> pentahedron
4914           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4915             // iBeforeSame is same too
4916             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4917                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4918                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4919           else
4920             // iAfterSame is same too
4921             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4922                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4923                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4924         }
4925         break;
4926       }
4927       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4928       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4929         if ( nbDouble+nbSame != 3 ) break;
4930         if(nbSame==0) {
4931           // --->  pentahedron with 15 nodes
4932           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4933                                        nextNod[0], nextNod[1], nextNod[2],
4934                                        prevNod[3], prevNod[4], prevNod[5],
4935                                        nextNod[3], nextNod[4], nextNod[5],
4936                                        midlNod[0], midlNod[1], midlNod[2]);
4937         }
4938         else if(nbSame==1) {
4939           // --->  2d order pyramid of 13 nodes
4940           int apex = iSameNode;
4941           int i0 = ( apex + 1 ) % nbCorners;
4942           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4943           int i0a = apex + 3;
4944           int i1a = i1 + 3;
4945           int i01 = i0 + 3;
4946           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4947                                       nextNod[i0], nextNod[i1], prevNod[apex],
4948                                       prevNod[i01], midlNod[i0],
4949                                       nextNod[i01], midlNod[i1],
4950                                       prevNod[i1a], prevNod[i0a],
4951                                       nextNod[i0a], nextNod[i1a]);
4952         }
4953         else if(nbSame==2) {
4954           // --->  2d order tetrahedron of 10 nodes
4955           int n1 = iNotSameNode;
4956           int n2 = ( n1 + 1             ) % nbCorners;
4957           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4958           int n12 = n1 + 3;
4959           int n23 = n2 + 3;
4960           int n31 = n3 + 3;
4961           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4962                                        prevNod[n12], prevNod[n23], prevNod[n31],
4963                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4964         }
4965         break;
4966       }
4967       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4968         if( nbSame == 0 ) {
4969           if ( nbDouble != 4 ) break;
4970           // --->  hexahedron with 20 nodes
4971           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4972                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4973                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4974                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4975                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4976         }
4977         else if(nbSame==1) {
4978           // ---> pyramid + pentahedron - can not be created since it is needed
4979           // additional middle node at the center of face
4980           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4981           return;
4982         }
4983         else if( nbSame == 2 ) {
4984           if ( nbDouble != 2 ) break;
4985           // --->  2d order Pentahedron with 15 nodes
4986           int n1,n2,n4,n5;
4987           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4988             // iBeforeSame is same too
4989             n1 = iBeforeSame;
4990             n2 = iOpposSame;
4991             n4 = iSameNode;
4992             n5 = iAfterSame;
4993           }
4994           else {
4995             // iAfterSame is same too
4996             n1 = iSameNode;
4997             n2 = iBeforeSame;
4998             n4 = iAfterSame;
4999             n5 = iOpposSame;
5000           }
5001           int n12 = n2 + 4;
5002           int n45 = n4 + 4;
5003           int n14 = n1 + 4;
5004           int n25 = n5 + 4;
5005           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
5006                                        prevNod[n4], prevNod[n5], nextNod[n5],
5007                                        prevNod[n12], midlNod[n2], nextNod[n12],
5008                                        prevNod[n45], midlNod[n5], nextNod[n45],
5009                                        prevNod[n14], prevNod[n25], nextNod[n25]);
5010         }
5011         break;
5012       }
5013       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
5014
5015         if( nbSame == 0 && nbDouble == 9 ) {
5016           // --->  tri-quadratic hexahedron with 27 nodes
5017           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
5018                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
5019                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
5020                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
5021                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
5022                                        prevNod[8], // bottom center
5023                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
5024                                        nextNod[8], // top center
5025                                        midlNod[8]);// elem center
5026         }
5027         else
5028         {
5029           return;
5030         }
5031         break;
5032       }
5033       case SMDSEntity_Polygon: { // sweep POLYGON
5034
5035         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
5036           // --->  hexagonal prism
5037           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
5038                                        prevNod[3], prevNod[4], prevNod[5],
5039                                        nextNod[0], nextNod[1], nextNod[2],
5040                                        nextNod[3], nextNod[4], nextNod[5]);
5041         }
5042         break;
5043       }
5044       case SMDSEntity_Ball:
5045         return;
5046
5047       default:
5048         break;
5049       } // switch ( baseType )
5050     } // scope
5051
5052     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
5053     {
5054       if ( baseType != SMDSEntity_Polygon )
5055       {
5056         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
5057         SMDS_MeshCell::applyInterlace( ind, prevNod );
5058         SMDS_MeshCell::applyInterlace( ind, nextNod );
5059         SMDS_MeshCell::applyInterlace( ind, midlNod );
5060         SMDS_MeshCell::applyInterlace( ind, itNN );
5061         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
5062         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
5063       }
5064       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
5065       vector<int> quantities (nbNodes + 2);
5066       polyedre_nodes.clear();
5067       quantities.clear();
5068
5069       // bottom of prism
5070       for (int inode = 0; inode < nbNodes; inode++)
5071         polyedre_nodes.push_back( prevNod[inode] );
5072       quantities.push_back( nbNodes );
5073
5074       // top of prism
5075       polyedre_nodes.push_back( nextNod[0] );
5076       for (int inode = nbNodes; inode-1; --inode )
5077         polyedre_nodes.push_back( nextNod[inode-1] );
5078       quantities.push_back( nbNodes );
5079
5080       // side faces
5081       // 3--6--2
5082       // |     |
5083       // 7     5
5084       // |     |
5085       // 0--4--1
5086       const int iQuad = elem->IsQuadratic();
5087       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
5088       {
5089         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
5090         int inextface = (iface+1+iQuad) % nbNodes;
5091         int imid      = (iface+1) % nbNodes;
5092         polyedre_nodes.push_back( prevNod[inextface] );         // 0
5093         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
5094         polyedre_nodes.push_back( prevNod[iface] );             // 1
5095         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
5096         {
5097           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
5098           polyedre_nodes.push_back( nextNod[iface] );                         // 2
5099         }
5100         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
5101         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
5102         {
5103           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
5104           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
5105         }
5106         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
5107         if ( nbFaceNodes > 2 )
5108           quantities.push_back( nbFaceNodes );
5109         else // degenerated face
5110           polyedre_nodes.resize( prevNbNodes );
5111       }
5112       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
5113
5114     } // try to create a polyherdal prism
5115
5116     if ( aNewElem ) {
5117       newElems.push_back( aNewElem );
5118       myLastCreatedElems.push_back(aNewElem);
5119       srcElements.push_back( elem );
5120     }
5121
5122     // set new prev nodes
5123     for ( iNode = 0; iNode < nbNodes; iNode++ )
5124       prevNod[ iNode ] = nextNod[ iNode ];
5125
5126   } // loop on steps
5127 }
5128
5129 //=======================================================================
5130 /*!
5131  * \brief Create 1D and 2D elements around swept elements
5132  * \param mapNewNodes - source nodes and ones generated from them
5133  * \param newElemsMap - source elements and ones generated from them
5134  * \param elemNewNodesMap - nodes generated from each node of each element
5135  * \param elemSet - all swept elements
5136  * \param nbSteps - number of sweeping steps
5137  * \param srcElements - to append elem for each generated element
5138  */
5139 //=======================================================================
5140
5141 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
5142                                   TTElemOfElemListMap &    newElemsMap,
5143                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
5144                                   TIDSortedElemSet&        elemSet,
5145                                   const int                nbSteps,
5146                                   SMESH_SequenceOfElemPtr& srcElements)
5147 {
5148   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5149   SMESHDS_Mesh* aMesh = GetMeshDS();
5150
5151   // Find nodes belonging to only one initial element - sweep them into edges.
5152
5153   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5154   for ( ; nList != mapNewNodes.end(); nList++ )
5155   {
5156     const SMDS_MeshNode* node =
5157       static_cast<const SMDS_MeshNode*>( nList->first );
5158     if ( newElemsMap.count( node ))
5159       continue; // node was extruded into edge
5160     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5161     int nbInitElems = 0;
5162     const SMDS_MeshElement* el = 0;
5163     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5164     while ( eIt->more() && nbInitElems < 2 ) {
5165       const SMDS_MeshElement* e = eIt->next();
5166       SMDSAbs_ElementType  type = e->GetType();
5167       if ( type == SMDSAbs_Volume ||
5168            type < highType ||
5169            !elemSet.count(e))
5170         continue;
5171       if ( type > highType ) {
5172         nbInitElems = 0;
5173         highType    = type;
5174       }
5175       el = e;
5176       ++nbInitElems;
5177     }
5178     if ( nbInitElems == 1 ) {
5179       bool NotCreateEdge = el && el->IsMediumNode(node);
5180       if(!NotCreateEdge) {
5181         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5182         list<const SMDS_MeshElement*> newEdges;
5183         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5184       }
5185     }
5186   }
5187
5188   // Make a ceiling for each element ie an equal element of last new nodes.
5189   // Find free links of faces - make edges and sweep them into faces.
5190
5191   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5192
5193   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5194   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5195   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5196   {
5197     const SMDS_MeshElement* elem = itElem->first;
5198     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5199
5200     if(itElem->second.size()==0) continue;
5201
5202     const bool isQuadratic = elem->IsQuadratic();
5203
5204     if ( elem->GetType() == SMDSAbs_Edge ) {
5205       // create a ceiling edge
5206       if ( !isQuadratic ) {
5207         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5208                                vecNewNodes[ 1 ]->second.back())) {
5209           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5210                                                       vecNewNodes[ 1 ]->second.back()));
5211           srcElements.push_back( elem );
5212         }
5213       }
5214       else {
5215         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5216                                vecNewNodes[ 1 ]->second.back(),
5217                                vecNewNodes[ 2 ]->second.back())) {
5218           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5219                                                       vecNewNodes[ 1 ]->second.back(),
5220                                                       vecNewNodes[ 2 ]->second.back()));
5221           srcElements.push_back( elem );
5222         }
5223       }
5224     }
5225     if ( elem->GetType() != SMDSAbs_Face )
5226       continue;
5227
5228     bool hasFreeLinks = false;
5229
5230     TIDSortedElemSet avoidSet;
5231     avoidSet.insert( elem );
5232
5233     set<const SMDS_MeshNode*> aFaceLastNodes;
5234     int iNode, nbNodes = vecNewNodes.size();
5235     if ( !isQuadratic ) {
5236       // loop on the face nodes
5237       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5238         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5239         // look for free links of the face
5240         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5241         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5242         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5243         // check if a link n1-n2 is free
5244         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5245           hasFreeLinks = true;
5246           // make a new edge and a ceiling for a new edge
5247           const SMDS_MeshElement* edge;
5248           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5249             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5250             srcElements.push_back( myLastCreatedElems.back() );
5251           }
5252           n1 = vecNewNodes[ iNode ]->second.back();
5253           n2 = vecNewNodes[ iNext ]->second.back();
5254           if ( !aMesh->FindEdge( n1, n2 )) {
5255             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5256             srcElements.push_back( edge );
5257           }
5258         }
5259       }
5260     }
5261     else { // elem is quadratic face
5262       int nbn = nbNodes/2;
5263       for ( iNode = 0; iNode < nbn; iNode++ ) {
5264         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5265         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5266         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5267         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5268         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5269         // check if a link is free
5270         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5271              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5272              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5273           hasFreeLinks = true;
5274           // make an edge and a ceiling for a new edge
5275           // find medium node
5276           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5277             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5278             srcElements.push_back( elem );
5279           }
5280           n1 = vecNewNodes[ iNode ]->second.back();
5281           n2 = vecNewNodes[ iNext ]->second.back();
5282           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5283           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5284             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5285             srcElements.push_back( elem );
5286           }
5287         }
5288       }
5289       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5290         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5291       }
5292     }
5293
5294     // sweep free links into faces
5295
5296     if ( hasFreeLinks ) {
5297       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5298       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5299
5300       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5301       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5302       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5303         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5304         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5305       }
5306       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5307         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5308         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5309       }
5310       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5311         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5312         std::advance( v, volNb );
5313         // find indices of free faces of a volume and their source edges
5314         list< int > freeInd;
5315         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5316         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5317         int iF, nbF = vTool.NbFaces();
5318         for ( iF = 0; iF < nbF; iF ++ ) {
5319           if ( vTool.IsFreeFace( iF ) &&
5320                vTool.GetFaceNodes( iF, faceNodeSet ) &&
5321                initNodeSet != faceNodeSet) // except an initial face
5322           {
5323             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5324               continue;
5325             if ( faceNodeSet == initNodeSetNoCenter )
5326               continue;
5327             freeInd.push_back( iF );
5328             // find source edge of a free face iF
5329             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5330             vector<const SMDS_MeshNode*>::iterator lastCommom;
5331             commonNodes.resize( nbNodes, 0 );
5332             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5333                                                 initNodeSet.begin(), initNodeSet.end(),
5334                                                 commonNodes.begin());
5335             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5336               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5337             else
5338               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5339
5340             if (SALOME::VerbosityActivated() && !srcEdges.back())
5341             {
5342               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5343                   << iF << " of volume #" << vTool.ID() << endl;
5344             }
5345           }
5346         }
5347         if ( freeInd.empty() )
5348           continue;
5349
5350         // create wall faces for all steps;
5351         // if such a face has been already created by sweep of edge,
5352         // assure that its orientation is OK
5353         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5354         {
5355           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5356           vTool.SetExternalNormal();
5357           const int nextShift = vTool.IsForward() ? +1 : -1;
5358           list< int >::iterator ind = freeInd.begin();
5359           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5360           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5361           {
5362             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5363             int nbn = vTool.NbFaceNodes( *ind );
5364             const SMDS_MeshElement * f = 0;
5365             if ( nbn == 3 )              ///// triangle
5366             {
5367               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5368               if ( !f ||
5369                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5370               {
5371                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5372                                                      nodes[ 1 ],
5373                                                      nodes[ 1 + nextShift ] };
5374                 if ( f )
5375                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5376                 else
5377                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5378                                                                newOrder[ 2 ] ));
5379               }
5380             }
5381             else if ( nbn == 4 )       ///// quadrangle
5382             {
5383               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5384               if ( !f ||
5385                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5386               {
5387                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5388                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5389                 if ( f )
5390                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5391                 else
5392                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5393                                                                newOrder[ 2 ], newOrder[ 3 ]));
5394               }
5395             }
5396             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5397             {
5398               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5399               if ( !f ||
5400                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5401               {
5402                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5403                                                      nodes[2],
5404                                                      nodes[2 + 2*nextShift],
5405                                                      nodes[3 - 2*nextShift],
5406                                                      nodes[3],
5407                                                      nodes[3 + 2*nextShift]};
5408                 if ( f )
5409                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5410                 else
5411                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5412                                                                newOrder[ 1 ],
5413                                                                newOrder[ 2 ],
5414                                                                newOrder[ 3 ],
5415                                                                newOrder[ 4 ],
5416                                                                newOrder[ 5 ] ));
5417               }
5418             }
5419             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5420             {
5421               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5422                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5423               if ( !f ||
5424                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5425               {
5426                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5427                                                      nodes[4 - 2*nextShift],
5428                                                      nodes[4],
5429                                                      nodes[4 + 2*nextShift],
5430                                                      nodes[1],
5431                                                      nodes[5 - 2*nextShift],
5432                                                      nodes[5],
5433                                                      nodes[5 + 2*nextShift] };
5434                 if ( f )
5435                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5436                 else
5437                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5438                                                               newOrder[ 2 ], newOrder[ 3 ],
5439                                                               newOrder[ 4 ], newOrder[ 5 ],
5440                                                               newOrder[ 6 ], newOrder[ 7 ]));
5441               }
5442             }
5443             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5444             {
5445               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5446                                       SMDSAbs_Face, /*noMedium=*/false);
5447               if ( !f ||
5448                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5449               {
5450                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5451                                                      nodes[4 - 2*nextShift],
5452                                                      nodes[4],
5453                                                      nodes[4 + 2*nextShift],
5454                                                      nodes[1],
5455                                                      nodes[5 - 2*nextShift],
5456                                                      nodes[5],
5457                                                      nodes[5 + 2*nextShift],
5458                                                      nodes[8] };
5459                 if ( f )
5460                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5461                 else
5462                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5463                                                               newOrder[ 2 ], newOrder[ 3 ],
5464                                                               newOrder[ 4 ], newOrder[ 5 ],
5465                                                               newOrder[ 6 ], newOrder[ 7 ],
5466                                                               newOrder[ 8 ]));
5467               }
5468             }
5469             else  //////// polygon
5470             {
5471               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5472               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5473               if ( !f ||
5474                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5475               {
5476                 if ( !vTool.IsForward() )
5477                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5478                 if ( f )
5479                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5480                 else
5481                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5482               }
5483             }
5484
5485             while ( srcElements.size() < myLastCreatedElems.size() )
5486               srcElements.push_back( *srcEdge );
5487
5488           }  // loop on free faces
5489
5490           // go to the next volume
5491           iVol = 0;
5492           while ( iVol++ < nbVolumesByStep ) v++;
5493
5494         } // loop on steps
5495       } // loop on volumes of one step
5496     } // sweep free links into faces
5497
5498     // Make a ceiling face with a normal external to a volume
5499
5500     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5501     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5502     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5503
5504     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5505       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5506       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5507     }
5508     if ( iF >= 0 )
5509     {
5510       lastVol.SetExternalNormal();
5511       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5512       const               int nbn = lastVol.NbFaceNodes( iF );
5513       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5514       if ( !hasFreeLinks ||
5515            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5516       {
5517         const vector<int>& interlace =
5518           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5519         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5520
5521         AddElement( nodeVec, anyFace.Init( elem ));
5522
5523         while ( srcElements.size() < myLastCreatedElems.size() )
5524           srcElements.push_back( elem );
5525       }
5526     }
5527   } // loop on swept elements
5528 }
5529
5530 //=======================================================================
5531 //function : RotationSweep
5532 //purpose  :
5533 //=======================================================================
5534
5535 SMESH_MeshEditor::PGroupIDs
5536 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5537                                 const gp_Ax1&      theAxis,
5538                                 const double       theAngle,
5539                                 const int          theNbSteps,
5540                                 const double       theTol,
5541                                 const bool         theMakeGroups,
5542                                 const bool         theMakeWalls)
5543 {
5544   ClearLastCreated();
5545
5546   setElemsFirst( theElemSets );
5547   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5548   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5549
5550   // source elements for each generated one
5551   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5552   srcElems.reserve( theElemSets[0].size() );
5553   srcNodes.reserve( theElemSets[1].size() );
5554
5555   gp_Trsf aTrsf;
5556   aTrsf.SetRotation( theAxis, theAngle );
5557   gp_Trsf aTrsf2;
5558   aTrsf2.SetRotation( theAxis, theAngle/2. );
5559
5560   gp_Lin aLine( theAxis );
5561   double aSqTol = theTol * theTol;
5562
5563   SMESHDS_Mesh* aMesh = GetMeshDS();
5564
5565   TNodeOfNodeListMap mapNewNodes;
5566   TElemOfVecOfNnlmiMap mapElemNewNodes;
5567   TTElemOfElemListMap newElemsMap;
5568
5569   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5570                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5571                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5572   // loop on theElemSets
5573   TIDSortedElemSet::iterator itElem;
5574   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5575   {
5576     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5577     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5578       const SMDS_MeshElement* elem = *itElem;
5579       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5580         continue;
5581       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5582       newNodesItVec.reserve( elem->NbNodes() );
5583
5584       // loop on elem nodes
5585       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5586       while ( itN->more() )
5587       {
5588         const SMDS_MeshNode* node = cast2Node( itN->next() );
5589
5590         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5591         double coord[3];
5592         aXYZ.Coord( coord[0], coord[1], coord[2] );
5593         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5594
5595         // check if a node has been already sweeped
5596         TNodeOfNodeListMapItr nIt =
5597           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5598         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5599         if ( listNewNodes.empty() )
5600         {
5601           // check if we are to create medium nodes between corner ones
5602           bool needMediumNodes = false;
5603           if ( isQuadraticMesh )
5604           {
5605             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5606             while (it->more() && !needMediumNodes )
5607             {
5608               const SMDS_MeshElement* invElem = it->next();
5609               if ( invElem != elem && !theElems.count( invElem )) continue;
5610               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5611               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5612                 needMediumNodes = true;
5613             }
5614           }
5615
5616           // make new nodes
5617           const SMDS_MeshNode * newNode = node;
5618           for ( int i = 0; i < theNbSteps; i++ ) {
5619             if ( !isOnAxis ) {
5620               if ( needMediumNodes )  // create a medium node
5621               {
5622                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5623                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5624                 myLastCreatedNodes.push_back(newNode);
5625                 srcNodes.push_back( node );
5626                 listNewNodes.push_back( newNode );
5627                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5628               }
5629               else {
5630                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5631               }
5632               // create a corner node
5633               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5634               myLastCreatedNodes.push_back(newNode);
5635               srcNodes.push_back( node );
5636               listNewNodes.push_back( newNode );
5637             }
5638             else {
5639               listNewNodes.push_back( newNode );
5640               // if ( needMediumNodes )
5641               //   listNewNodes.push_back( newNode );
5642             }
5643           }
5644         }
5645         newNodesItVec.push_back( nIt );
5646       }
5647       // make new elements
5648       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5649     }
5650   }
5651
5652   if ( theMakeWalls )
5653     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5654
5655   PGroupIDs newGroupIDs;
5656   if ( theMakeGroups )
5657     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5658
5659   return newGroupIDs;
5660 }
5661
5662 //=======================================================================
5663 //function : ExtrusParam
5664 //purpose  : standard construction
5665 //=======================================================================
5666
5667 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5668                                             const int                theNbSteps,
5669                                             const std::list<double>& theScales,
5670                                             const std::list<double>& theAngles,
5671                                             const gp_XYZ*            theBasePoint,
5672                                             const int                theFlags,
5673                                             const double             theTolerance):
5674   myDir( theStep ),
5675   myBaseP( Precision::Infinite(), 0, 0 ),
5676   myFlags( theFlags ),
5677   myTolerance( theTolerance ),
5678   myElemsToUse( NULL )
5679 {
5680   mySteps = new TColStd_HSequenceOfReal;
5681   const double stepSize = theStep.Magnitude();
5682   for (int i=1; i<=theNbSteps; i++ )
5683     mySteps->Append( stepSize );
5684
5685   if ( !theScales.empty() )
5686   {
5687     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5688       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5689
5690     // add medium scales
5691     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5692     myScales.reserve( theNbSteps * 2 );
5693     myScales.push_back( 0.5 * ( *s1 + 1. ));
5694     myScales.push_back( *s1 );
5695     for ( ; s2 != theScales.end(); s1 = s2++ )
5696     {
5697       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5698       myScales.push_back( *s2 );
5699     }
5700   }
5701
5702   if ( !theAngles.empty() )
5703   {
5704     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5705     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5706       linearAngleVariation( theNbSteps, angles );
5707
5708     // accumulate angles
5709     double angle = 0;
5710     int nbAngles = 0;
5711     std::list<double>::iterator a1 = angles.begin(), a2;
5712     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5713     {
5714       angle += *a1;
5715       *a1 = angle;
5716     }
5717     while ( nbAngles++ < theNbSteps )
5718       angles.push_back( angles.back() );
5719
5720     // add medium angles
5721     a2 = angles.begin(), a1 = a2++;
5722     myAngles.push_back( 0.5 * *a1 );
5723     myAngles.push_back( *a1 );
5724     for ( ; a2 != angles.end(); a1 = a2++ )
5725     {
5726       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5727       myAngles.push_back( *a2 );
5728     }
5729   }
5730
5731   if ( theBasePoint )
5732   {
5733     myBaseP = *theBasePoint;
5734   }
5735
5736   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5737       ( theTolerance > 0 ))
5738   {
5739     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5740   }
5741   else
5742   {
5743     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5744   }
5745 }
5746
5747 //=======================================================================
5748 //function : ExtrusParam
5749 //purpose  : steps are given explicitly
5750 //=======================================================================
5751
5752 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5753                                             Handle(TColStd_HSequenceOfReal) theSteps,
5754                                             const int                       theFlags,
5755                                             const double                    theTolerance):
5756   myDir( theDir ),
5757   mySteps( theSteps ),
5758   myFlags( theFlags ),
5759   myTolerance( theTolerance ),
5760   myElemsToUse( NULL )
5761 {
5762   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5763       ( theTolerance > 0 ))
5764   {
5765     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5766   }
5767   else
5768   {
5769     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5770   }
5771 }
5772
5773 //=======================================================================
5774 //function : ExtrusParam
5775 //purpose  : for extrusion by normal
5776 //=======================================================================
5777
5778 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5779                                             const int    theNbSteps,
5780                                             const int    theFlags,
5781                                             const int    theDim ):
5782   myDir( 1,0,0 ),
5783   mySteps( new TColStd_HSequenceOfReal ),
5784   myFlags( theFlags ),
5785   myTolerance( 0 ),
5786   myElemsToUse( NULL )
5787 {
5788   for (int i = 0; i < theNbSteps; i++ )
5789     mySteps->Append( theStepSize );
5790
5791   if ( theDim == 1 )
5792   {
5793     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5794   }
5795   else
5796   {
5797     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5798   }
5799 }
5800
5801 //=======================================================================
5802 //function : ExtrusParam
5803 //purpose  : for extrusion along path
5804 //=======================================================================
5805
5806 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5807                                             const gp_Pnt*                   theBasePoint,
5808                                             const std::list<double>&        theScales,
5809                                             const bool                      theMakeGroups )
5810   : myBaseP( Precision::Infinite(), 0, 0 ),
5811     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5812     myPathPoints( thePoints )
5813 {
5814   if ( theBasePoint )
5815   {
5816     myBaseP = theBasePoint->XYZ();
5817   }
5818
5819   if ( !theScales.empty() )
5820   {
5821     // add medium scales
5822     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5823     myScales.reserve( thePoints.size() * 2 );
5824     myScales.push_back( 0.5 * ( 1. + *s1 ));
5825     myScales.push_back( *s1 );
5826     for ( ; s2 != theScales.end(); s1 = s2++ )
5827     {
5828       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5829       myScales.push_back( *s2 );
5830     }
5831   }
5832
5833   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5834 }
5835
5836 //=======================================================================
5837 //function : ExtrusParam::SetElementsToUse
5838 //purpose  : stores elements to use for extrusion by normal, depending on
5839 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5840 //           define myBaseP for scaling
5841 //=======================================================================
5842
5843 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5844                                                       const TIDSortedElemSet& nodes )
5845 {
5846   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5847
5848   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5849   {
5850     myBaseP.SetCoord( 0.,0.,0. );
5851     TIDSortedElemSet newNodes;
5852
5853     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5854     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5855     {
5856       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5857       TIDSortedElemSet::const_iterator itElem = elements.begin();
5858       for ( ; itElem != elements.end(); itElem++ )
5859       {
5860         const SMDS_MeshElement* elem = *itElem;
5861         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5862         while ( itN->more() ) {
5863           const SMDS_MeshElement* node = itN->next();
5864           if ( newNodes.insert( node ).second )
5865             myBaseP += SMESH_NodeXYZ( node );
5866         }
5867       }
5868     }
5869     myBaseP /= newNodes.size();
5870   }
5871 }
5872
5873 //=======================================================================
5874 //function : ExtrusParam::beginStepIter
5875 //purpose  : prepare iteration on steps
5876 //=======================================================================
5877
5878 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5879 {
5880   myWithMediumNodes = withMediumNodes;
5881   myNextStep = 1;
5882   myCurSteps.clear();
5883 }
5884 //=======================================================================
5885 //function : ExtrusParam::moreSteps
5886 //purpose  : are there more steps?
5887 //=======================================================================
5888
5889 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5890 {
5891   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5892 }
5893 //=======================================================================
5894 //function : ExtrusParam::nextStep
5895 //purpose  : returns the next step
5896 //=======================================================================
5897
5898 double SMESH_MeshEditor::ExtrusParam::nextStep()
5899 {
5900   double res = 0;
5901   if ( !myCurSteps.empty() )
5902   {
5903     res = myCurSteps.back();
5904     myCurSteps.pop_back();
5905   }
5906   else if ( myNextStep <= mySteps->Length() )
5907   {
5908     myCurSteps.push_back( mySteps->Value( myNextStep ));
5909     ++myNextStep;
5910     if ( myWithMediumNodes )
5911     {
5912       myCurSteps.back() /= 2.;
5913       myCurSteps.push_back( myCurSteps.back() );
5914     }
5915     res = nextStep();
5916   }
5917   return res;
5918 }
5919
5920 //=======================================================================
5921 //function : ExtrusParam::makeNodesByDir
5922 //purpose  : create nodes for standard extrusion
5923 //=======================================================================
5924
5925 int SMESH_MeshEditor::ExtrusParam::
5926 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5927                 const SMDS_MeshNode*              srcNode,
5928                 std::list<const SMDS_MeshNode*> & newNodes,
5929                 const bool                        makeMediumNodes)
5930 {
5931   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5932
5933   int nbNodes = 0;
5934   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5935   {
5936     p += myDir.XYZ() * nextStep();
5937     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5938     newNodes.push_back( newNode );
5939   }
5940
5941   if ( !myScales.empty() || !myAngles.empty() )
5942   {
5943     gp_XYZ  center = myBaseP;
5944     gp_Ax1  ratationAxis( center, myDir );
5945     gp_Trsf rotation;
5946
5947     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5948     size_t i = !makeMediumNodes;
5949     for ( beginStepIter( makeMediumNodes );
5950           moreSteps();
5951           ++nIt, i += 1 + !makeMediumNodes )
5952     {
5953       center += myDir.XYZ() * nextStep();
5954
5955       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5956       bool moved = false;
5957       if ( i < myScales.size() )
5958       {
5959         xyz = ( myScales[i] * ( xyz - center )) + center;
5960         moved = true;
5961       }
5962       if ( !myAngles.empty() )
5963       {
5964         rotation.SetRotation( ratationAxis, myAngles[i] );
5965         rotation.Transforms( xyz );
5966         moved = true;
5967       }
5968       if ( moved )
5969         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5970       else
5971         break;
5972     }
5973   }
5974   return nbNodes;
5975 }
5976
5977 //=======================================================================
5978 //function : ExtrusParam::makeNodesByDirAndSew
5979 //purpose  : create nodes for standard extrusion with sewing
5980 //=======================================================================
5981
5982 int SMESH_MeshEditor::ExtrusParam::
5983 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5984                       const SMDS_MeshNode*              srcNode,
5985                       std::list<const SMDS_MeshNode*> & newNodes,
5986                       const bool                        makeMediumNodes)
5987 {
5988   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5989
5990   int nbNodes = 0;
5991   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5992   {
5993     P1 += myDir.XYZ() * nextStep();
5994
5995     // try to search in sequence of existing nodes
5996     // if myNodes.size()>0 we 'nave to use given sequence
5997     // else - use all nodes of mesh
5998     const SMDS_MeshNode * node = 0;
5999     if ( myNodes.Length() > 0 )
6000     {
6001       for ( int i = 1; i <= myNodes.Length(); i++ )
6002       {
6003         SMESH_NodeXYZ P2 = myNodes.Value(i);
6004         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6005         {
6006           node = myNodes.Value(i);
6007           break;
6008         }
6009       }
6010     }
6011     else
6012     {
6013       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
6014       while(itn->more())
6015       {
6016         SMESH_NodeXYZ P2 = itn->next();
6017         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6018         {
6019           node = P2._node;
6020           break;
6021         }
6022       }
6023     }
6024
6025     if ( !node )
6026       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
6027
6028     newNodes.push_back( node );
6029
6030   } // loop on steps
6031
6032   return nbNodes;
6033 }
6034
6035 //=======================================================================
6036 //function : ExtrusParam::makeNodesByNormal2D
6037 //purpose  : create nodes for extrusion using normals of faces
6038 //=======================================================================
6039
6040 int SMESH_MeshEditor::ExtrusParam::
6041 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
6042                      const SMDS_MeshNode*              srcNode,
6043                      std::list<const SMDS_MeshNode*> & newNodes,
6044                      const bool                        makeMediumNodes)
6045 {
6046   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
6047
6048   gp_XYZ p = SMESH_NodeXYZ( srcNode );
6049
6050   // get normals to faces sharing srcNode
6051   vector< gp_XYZ > norms, baryCenters;
6052   gp_XYZ norm, avgNorm( 0,0,0 );
6053   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
6054   while ( faceIt->more() )
6055   {
6056     const SMDS_MeshElement* face = faceIt->next();
6057     if ( myElemsToUse && !myElemsToUse->count( face ))
6058       continue;
6059     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
6060     {
6061       norms.push_back( norm );
6062       avgNorm += norm;
6063       if ( !alongAvgNorm )
6064       {
6065         gp_XYZ bc(0,0,0);
6066         int nbN = 0;
6067         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
6068           bc += SMESH_NodeXYZ( nIt->next() );
6069         baryCenters.push_back( bc / nbN );
6070       }
6071     }
6072   }
6073
6074   if ( norms.empty() ) return 0;
6075
6076   double normSize = avgNorm.Modulus();
6077   if ( normSize < std::numeric_limits<double>::min() )
6078     return 0;
6079
6080   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
6081   {
6082     myDir = avgNorm;
6083     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
6084   }
6085
6086   avgNorm /= normSize;
6087
6088   int nbNodes = 0;
6089   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
6090   {
6091     gp_XYZ pNew = p;
6092     double stepSize = nextStep();
6093
6094     if ( norms.size() > 1 )
6095     {
6096       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
6097       {
6098         // translate plane of a face
6099         baryCenters[ iF ] += norms[ iF ] * stepSize;
6100
6101         // find point of intersection of the face plane located at baryCenters[ iF ]
6102         // and avgNorm located at pNew
6103         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
6104         double dot  = ( norms[ iF ] * avgNorm );
6105         if ( dot < std::numeric_limits<double>::min() )
6106           dot = stepSize * 1e-3;
6107         double step = -( norms[ iF ] * pNew + d ) / dot;
6108         pNew += step * avgNorm;
6109       }
6110     }
6111     else
6112     {
6113       pNew += stepSize * avgNorm;
6114     }
6115     p = pNew;
6116
6117     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6118     newNodes.push_back( newNode );
6119   }
6120   return nbNodes;
6121 }
6122
6123 //=======================================================================
6124 //function : ExtrusParam::makeNodesByNormal1D
6125 //purpose  : create nodes for extrusion using normals of edges
6126 //=======================================================================
6127
6128 int SMESH_MeshEditor::ExtrusParam::
6129 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
6130                      const SMDS_MeshNode*              /*srcNode*/,
6131                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
6132                      const bool                        /*makeMediumNodes*/)
6133 {
6134   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6135   return 0;
6136 }
6137
6138 //=======================================================================
6139 //function : ExtrusParam::makeNodesAlongTrack
6140 //purpose  : create nodes for extrusion along path
6141 //=======================================================================
6142
6143 int SMESH_MeshEditor::ExtrusParam::
6144 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
6145                      const SMDS_MeshNode*              srcNode,
6146                      std::list<const SMDS_MeshNode*> & newNodes,
6147                      const bool                        makeMediumNodes)
6148 {
6149   const Standard_Real aTolAng=1.e-4;
6150
6151   gp_Pnt aV0x = myBaseP;
6152   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6153
6154   const PathPoint& aPP0 = myPathPoints[0];
6155   gp_Pnt aP0x = aPP0.myPnt;
6156   gp_Dir aDT0x= aPP0.myTgt;
6157
6158   std::vector< gp_Pnt > centers;
6159   centers.reserve( NbSteps() * 2 );
6160
6161   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6162
6163   for ( size_t j = 1; j < myPathPoints.size(); ++j )
6164   {
6165     const PathPoint&  aPP  = myPathPoints[j];
6166     const gp_Pnt&     aP1x = aPP.myPnt;
6167     const gp_Dir&    aDT1x = aPP.myTgt;
6168
6169     // Translation
6170     gp_Vec aV01x( aP0x, aP1x );
6171     aTrsf.SetTranslation( aV01x );
6172     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6173     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6174
6175     // rotation 1 [ T1,T0 ]
6176     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6177     if ( fabs( aAngleT1T0 ) > aTolAng )
6178     {
6179       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6180       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6181
6182       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6183     }
6184
6185     // rotation 2
6186     if ( aPP.myAngle != 0. )
6187     {
6188       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6189       aPN1 = aPN1.Transformed( aTrsfRot );
6190     }
6191
6192     // make new node
6193     if ( makeMediumNodes )
6194     {
6195       // create additional node
6196       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6197       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6198       newNodes.push_back( newNode );
6199
6200     }
6201     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6202     newNodes.push_back( newNode );
6203
6204     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6205     centers.push_back( aV1x );
6206
6207     aPN0 = aPN1;
6208     aP0x = aP1x;
6209     aV0x = aV1x;
6210     aDT0x = aDT1x;
6211   }
6212
6213   // scale
6214   if ( !myScales.empty() )
6215   {
6216     gp_Trsf aTrsfScale;
6217     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6218     for ( size_t i = !makeMediumNodes;
6219           i < myScales.size() && node != newNodes.end();
6220           i += ( 1 + !makeMediumNodes ), ++node )
6221     {
6222       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6223       gp_Pnt aN = SMESH_NodeXYZ( *node );
6224       gp_Pnt aP = aN.Transformed( aTrsfScale );
6225       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6226     }
6227   }
6228
6229   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6230 }
6231
6232 //=======================================================================
6233 //function : ExtrusionSweep
6234 //purpose  :
6235 //=======================================================================
6236
6237 SMESH_MeshEditor::PGroupIDs
6238 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
6239                                   const gp_Vec&        theStep,
6240                                   const int            theNbSteps,
6241                                   TTElemOfElemListMap& newElemsMap,
6242                                   const int            theFlags,
6243                                   const double         theTolerance)
6244 {
6245   std::list<double> dummy;
6246   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6247                        theFlags, theTolerance );
6248   return ExtrusionSweep( theElems, aParams, newElemsMap );
6249 }
6250
6251 namespace
6252 {
6253
6254 //=======================================================================
6255 //function : getOriFactor
6256 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
6257 //           edge curve orientation
6258 //=======================================================================
6259
6260   double getOriFactor( const TopoDS_Edge&   edge,
6261                        const SMDS_MeshNode* n1,
6262                        const SMDS_MeshNode* n2,
6263                        SMESH_MesherHelper&  helper)
6264   {
6265     double u1 = helper.GetNodeU( edge, n1, n2 );
6266     double u2 = helper.GetNodeU( edge, n2, n1 );
6267     return u1 < u2 ? 1. : -1.;
6268   }
6269 }
6270
6271 //=======================================================================
6272 //function : ExtrusionSweep
6273 //purpose  :
6274 //=======================================================================
6275
6276 SMESH_MeshEditor::PGroupIDs
6277 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
6278                                   ExtrusParam&         theParams,
6279                                   TTElemOfElemListMap& newElemsMap)
6280 {
6281   ClearLastCreated();
6282
6283   setElemsFirst( theElemSets );
6284   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6285   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6286
6287   // source elements for each generated one
6288   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6289   srcElems.reserve( theElemSets[0].size() );
6290   srcNodes.reserve( theElemSets[1].size() );
6291
6292   const int nbSteps = theParams.NbSteps();
6293   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6294
6295   TNodeOfNodeListMap   mapNewNodes;
6296   TElemOfVecOfNnlmiMap mapElemNewNodes;
6297
6298   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6299                                      myMesh->NbFaces(ORDER_QUADRATIC) +
6300                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
6301   // loop on theElems
6302   TIDSortedElemSet::iterator itElem;
6303   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6304   {
6305     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6306     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6307     {
6308       // check element type
6309       const SMDS_MeshElement* elem = *itElem;
6310       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
6311         continue;
6312
6313       const size_t nbNodes = elem->NbNodes();
6314       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6315       newNodesItVec.reserve( nbNodes );
6316
6317       // loop on elem nodes
6318       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6319       while ( itN->more() )
6320       {
6321         // check if a node has been already sweeped
6322         const SMDS_MeshNode* node = itN->next();
6323         TNodeOfNodeListMap::iterator nIt =
6324           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6325         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6326         if ( listNewNodes.empty() )
6327         {
6328           // make new nodes
6329
6330           // check if we are to create medium nodes between corner ones
6331           bool needMediumNodes = false;
6332           if ( isQuadraticMesh )
6333           {
6334             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6335             while (it->more() && !needMediumNodes )
6336             {
6337               const SMDS_MeshElement* invElem = it->next();
6338               if ( invElem != elem && !theElems.count( invElem )) continue;
6339               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6340               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6341                 needMediumNodes = true;
6342             }
6343           }
6344           // create nodes for all steps
6345           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6346           {
6347             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6348             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6349             {
6350               myLastCreatedNodes.push_back( *newNodesIt );
6351               srcNodes.push_back( node );
6352             }
6353           }
6354           else
6355           {
6356             if ( theParams.ToMakeBoundary() )
6357             {
6358               GetMeshDS()->Modified();
6359               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6360             }
6361             break; // newNodesItVec will be shorter than nbNodes
6362           }
6363         }
6364         newNodesItVec.push_back( nIt );
6365       }
6366       // make new elements
6367       if ( newNodesItVec.size() == nbNodes )
6368         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6369     }
6370   }
6371
6372   if ( theParams.ToMakeBoundary() ) {
6373     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6374   }
6375   PGroupIDs newGroupIDs;
6376   if ( theParams.ToMakeGroups() )
6377     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6378
6379   return newGroupIDs;
6380 }
6381
6382 //=======================================================================
6383 //function : ExtrusionAlongTrack
6384 //purpose  :
6385 //=======================================================================
6386 SMESH_MeshEditor::Extrusion_Error
6387 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6388                                        SMESH_Mesh*          theTrackMesh,
6389                                        SMDS_ElemIteratorPtr theTrackIterator,
6390                                        const SMDS_MeshNode* theN1,
6391                                        std::list<double>&   theAngles,
6392                                        const bool           theAngleVariation,
6393                                        std::list<double>&   theScales,
6394                                        const bool           theScaleVariation,
6395                                        const gp_Pnt*        theRefPoint,
6396                                        const bool           theMakeGroups)
6397 {
6398   ClearLastCreated();
6399
6400   // 1. Check data
6401   if ( theElements[0].empty() && theElements[1].empty() )
6402     return EXTR_NO_ELEMENTS;
6403
6404   ASSERT( theTrackMesh );
6405   if ( ! theTrackIterator || !theTrackIterator->more() )
6406     return EXTR_NO_ELEMENTS;
6407
6408   // 2. Get ordered nodes
6409   SMESH_MeshAlgos::TElemGroupVector branchEdges;
6410   SMESH_MeshAlgos::TNodeGroupVector branchNods;
6411   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6412   if ( branchEdges.empty() )
6413     return EXTR_PATH_NOT_EDGE;
6414
6415   if ( branchEdges.size() > 1 )
6416     return EXTR_BAD_PATH_SHAPE;
6417
6418   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
6419   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6420   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6421     return EXTR_BAD_STARTING_NODE;
6422
6423   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6424   {
6425     // add medium nodes to pathNodes
6426     std::vector< const SMDS_MeshNode* >    pathNodes2;
6427     std::vector< const SMDS_MeshElement* > pathEdges2;
6428     pathNodes2.reserve( pathNodes.size() * 2 );
6429     pathEdges2.reserve( pathEdges.size() * 2 );
6430     for ( size_t i = 0; i < pathEdges.size(); ++i )
6431     {
6432       pathNodes2.push_back( pathNodes[i] );
6433       pathEdges2.push_back( pathEdges[i] );
6434       if ( pathEdges[i]->IsQuadratic() )
6435       {
6436         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6437         pathEdges2.push_back( pathEdges[i] );
6438       }
6439     }
6440     pathNodes2.push_back( pathNodes.back() );
6441     pathEdges.swap( pathEdges2 );
6442     pathNodes.swap( pathNodes2 );
6443   }
6444
6445   // 3. Get path data at pathNodes
6446
6447   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6448
6449   if ( theAngleVariation )
6450     linearAngleVariation( points.size()-1, theAngles );
6451   if ( theScaleVariation )
6452     linearScaleVariation( points.size()-1, theScales );
6453
6454   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6455   std::list<double>::iterator angle = theAngles.begin();
6456
6457   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6458
6459   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6460   std::map< int, double >::iterator id2factor;
6461   SMESH_MesherHelper pathHelper( *theTrackMesh );
6462   gp_Pnt p; gp_Vec tangent;
6463   const double tol2 = gp::Resolution() * gp::Resolution();
6464
6465   for ( size_t i = 0; i < pathNodes.size(); ++i )
6466   {
6467     ExtrusParam::PathPoint & point = points[ i ];
6468
6469     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6470
6471     if ( angle != theAngles.end() )
6472       point.myAngle = *angle++;
6473
6474     tangent.SetCoord( 0,0,0 );
6475     const int          shapeID = pathNodes[ i ]->GetShapeID();
6476     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6477     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6478     switch ( shapeType )
6479     {
6480     case TopAbs_EDGE:
6481     {
6482       TopoDS_Edge edge = TopoDS::Edge( shape );
6483       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6484       if ( id2factor->second == 0 )
6485       {
6486         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6487         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6488       }
6489       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6490       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6491       curve->D1( u, p, tangent );
6492       tangent *= id2factor->second;
6493       break;
6494     }
6495     case TopAbs_VERTEX:
6496     {
6497       int nbEdges = 0;
6498       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6499       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6500       {
6501         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6502         for ( int di = -1; di <= 0; ++di )
6503         {
6504           size_t j = i + di;
6505           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6506           {
6507             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6508             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6509             if ( id2factor->second == 0 )
6510             {
6511               if ( j < i )
6512                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6513               else
6514                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6515             }
6516             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6517             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6518             gp_Vec du;
6519             curve->D1( u, p, du );
6520             double size2 = du.SquareMagnitude();
6521             if ( du.SquareMagnitude() > tol2 )
6522             {
6523               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6524               nbEdges++;
6525             }
6526             break;
6527           }
6528         }
6529       }
6530       if ( nbEdges > 0 )
6531         break;
6532     }
6533     // fall through
6534     default:
6535     {
6536       for ( int di = -1; di <= 1; di += 2 )
6537       {
6538         size_t j = i + di;
6539         if ( j < pathNodes.size() )
6540         {
6541           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6542           double size2 = dir.SquareMagnitude();
6543           if ( size2 > tol2 )
6544             tangent += dir.Divided( Sqrt( size2 )) * di;
6545         }
6546       }
6547     }
6548     } // switch ( shapeType )
6549
6550     if ( tangent.SquareMagnitude() < tol2 )
6551       return EXTR_CANT_GET_TANGENT;
6552
6553     point.myTgt = tangent;
6554
6555   } // loop on pathNodes
6556
6557
6558   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6559   TTElemOfElemListMap newElemsMap;
6560
6561   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6562
6563   return EXTR_OK;
6564 }
6565
6566 //=======================================================================
6567 //function : linearAngleVariation
6568 //purpose  : spread values over nbSteps
6569 //=======================================================================
6570
6571 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6572                                             list<double>& Angles)
6573 {
6574   int nbAngles = Angles.size();
6575   if( nbSteps > nbAngles && nbAngles > 0 )
6576   {
6577     vector<double> theAngles(nbAngles);
6578     theAngles.assign( Angles.begin(), Angles.end() );
6579
6580     list<double> res;
6581     double rAn2St = double( nbAngles ) / double( nbSteps );
6582     double angPrev = 0, angle;
6583     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6584     {
6585       double angCur = rAn2St * ( iSt+1 );
6586       double angCurFloor  = floor( angCur );
6587       double angPrevFloor = floor( angPrev );
6588       if ( angPrevFloor == angCurFloor )
6589         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6590       else {
6591         int iP = int( angPrevFloor );
6592         double angPrevCeil = ceil(angPrev);
6593         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6594
6595         int iC = int( angCurFloor );
6596         if ( iC < nbAngles )
6597           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6598
6599         iP = int( angPrevCeil );
6600         while ( iC-- > iP )
6601           angle += theAngles[ iC ];
6602       }
6603       res.push_back(angle);
6604       angPrev = angCur;
6605     }
6606     Angles.swap( res );
6607   }
6608 }
6609
6610 //=======================================================================
6611 //function : linearScaleVariation
6612 //purpose  : spread values over nbSteps 
6613 //=======================================================================
6614
6615 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6616                                             std::list<double>& theScales)
6617 {
6618   int nbScales = theScales.size();
6619   std::vector<double> myScales;
6620   myScales.reserve( theNbSteps );
6621   std::list<double>::const_iterator scale = theScales.begin();
6622   double prevScale = 1.0;
6623   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6624   {
6625     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6626     int    stDelta = Max( 1, iStep - myScales.size());
6627     double scDelta = ( *scale - prevScale ) / stDelta;
6628     for ( int iStep = 0; iStep < stDelta; ++iStep )
6629     {
6630       myScales.push_back( prevScale + scDelta );
6631       prevScale = myScales.back();
6632     }
6633     prevScale = *scale;
6634   }
6635   theScales.assign( myScales.begin(), myScales.end() );
6636 }
6637
6638 //================================================================================
6639 /*!
6640  * \brief Move or copy theElements applying theTrsf to their nodes
6641  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6642  *  \param theTrsf - transformation to apply
6643  *  \param theCopy - if true, create translated copies of theElems
6644  *  \param theMakeGroups - if true and theCopy, create translated groups
6645  *  \param theTargetMesh - mesh to copy translated elements into
6646  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6647  */
6648 //================================================================================
6649
6650 SMESH_MeshEditor::PGroupIDs
6651 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6652                              const gp_Trsf&     theTrsf,
6653                              const bool         theCopy,
6654                              const bool         theMakeGroups,
6655                              SMESH_Mesh*        theTargetMesh)
6656 {
6657   ClearLastCreated();
6658   myLastCreatedElems.reserve( theElems.size() );
6659
6660   bool needReverse = false;
6661   string groupPostfix;
6662   switch ( theTrsf.Form() ) {
6663   case gp_PntMirror:
6664     needReverse = true;
6665     groupPostfix = "mirrored";
6666     break;
6667   case gp_Ax1Mirror:
6668     groupPostfix = "mirrored";
6669     break;
6670   case gp_Ax2Mirror:
6671     needReverse = true;
6672     groupPostfix = "mirrored";
6673     break;
6674   case gp_Rotation:
6675     groupPostfix = "rotated";
6676     break;
6677   case gp_Translation:
6678     groupPostfix = "translated";
6679     break;
6680   case gp_Scale:
6681     groupPostfix = "scaled";
6682     break;
6683   case gp_CompoundTrsf: // different scale by axis
6684     groupPostfix = "scaled";
6685     break;
6686   default:
6687     needReverse = false;
6688     groupPostfix = "transformed";
6689   }
6690
6691   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6692   SMESHDS_Mesh* aMesh    = GetMeshDS();
6693
6694   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6695   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6696   SMESH_MeshEditor::ElemFeatures elemType;
6697
6698   // map old node to new one
6699   TNodeNodeMap nodeMap;
6700
6701   // elements sharing moved nodes; those of them which have all
6702   // nodes mirrored but are not in theElems are to be reversed
6703   TIDSortedElemSet inverseElemSet;
6704
6705   // source elements for each generated one
6706   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6707
6708   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6709   TIDSortedElemSet orphanNode;
6710
6711   if ( theElems.empty() ) // transform the whole mesh
6712   {
6713     // add all elements
6714     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6715     while ( eIt->more() ) theElems.insert( eIt->next() );
6716     // add orphan nodes
6717     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6718     while ( nIt->more() )
6719     {
6720       const SMDS_MeshNode* node = nIt->next();
6721       if ( node->NbInverseElements() == 0)
6722         orphanNode.insert( node );
6723     }
6724   }
6725
6726   // loop on elements to transform nodes : first orphan nodes then elems
6727   TIDSortedElemSet::iterator itElem;
6728   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6729   for (int i=0; i<2; i++)
6730     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6731     {
6732       const SMDS_MeshElement* elem = *itElem;
6733       if ( !elem )
6734         continue;
6735
6736       // loop on elem nodes
6737       double coord[3];
6738       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6739       while ( itN->more() )
6740       {
6741         const SMDS_MeshNode* node = cast2Node( itN->next() );
6742         // check if a node has been already transformed
6743         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6744           nodeMap.insert( make_pair ( node, node ));
6745         if ( !n2n_isnew.second )
6746           continue;
6747
6748         node->GetXYZ( coord );
6749         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6750         if ( theTargetMesh ) {
6751           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6752           n2n_isnew.first->second = newNode;
6753           myLastCreatedNodes.push_back(newNode);
6754           srcNodes.push_back( node );
6755         }
6756         else if ( theCopy ) {
6757           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6758           n2n_isnew.first->second = newNode;
6759           myLastCreatedNodes.push_back(newNode);
6760           srcNodes.push_back( node );
6761         }
6762         else {
6763           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6764           // node position on shape becomes invalid
6765           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6766             ( SMDS_SpacePosition::originSpacePosition() );
6767         }
6768
6769         // keep inverse elements
6770         if ( !theCopy && !theTargetMesh && needReverse ) {
6771           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6772           while ( invElemIt->more() ) {
6773             const SMDS_MeshElement* iel = invElemIt->next();
6774             inverseElemSet.insert( iel );
6775           }
6776         }
6777       }
6778     } // loop on elems in { &orphanNode, &theElems };
6779
6780   // either create new elements or reverse mirrored ones
6781   if ( !theCopy && !needReverse && !theTargetMesh )
6782     return PGroupIDs();
6783
6784   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6785
6786   // Replicate or reverse elements
6787
6788   std::vector<int> iForw;
6789   vector<const SMDS_MeshNode*> nodes;
6790   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6791   {
6792     const SMDS_MeshElement* elem = *itElem;
6793     if ( !elem ) continue;
6794
6795     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6796     size_t               nbNodes  = elem->NbNodes();
6797     if ( geomType == SMDSGeom_NONE ) continue; // node
6798
6799     nodes.resize( nbNodes );
6800
6801     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6802     {
6803       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6804       if ( !aPolyedre )
6805         continue;
6806       nodes.clear();
6807       bool allTransformed = true;
6808       int nbFaces = aPolyedre->NbFaces();
6809       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6810       {
6811         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6812         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6813         {
6814           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6815           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6816           if ( nodeMapIt == nodeMap.end() )
6817             allTransformed = false; // not all nodes transformed
6818           else
6819             nodes.push_back((*nodeMapIt).second);
6820         }
6821         if ( needReverse && allTransformed )
6822           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6823       }
6824       if ( !allTransformed )
6825         continue; // not all nodes transformed
6826     }
6827     else // ----------------------- the rest element types
6828     {
6829       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6830       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6831       const vector<int>&    i = needReverse ? iRev : iForw;
6832
6833       // find transformed nodes
6834       size_t iNode = 0;
6835       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6836       while ( itN->more() ) {
6837         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6838         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6839         if ( nodeMapIt == nodeMap.end() )
6840           break; // not all nodes transformed
6841         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6842       }
6843       if ( iNode != nbNodes )
6844         continue; // not all nodes transformed
6845     }
6846
6847     if ( editor ) {
6848       // copy in this or a new mesh
6849       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6850         srcElems.push_back( elem );
6851     }
6852     else {
6853       // reverse element as it was reversed by transformation
6854       if ( nbNodes > 2 )
6855         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6856     }
6857
6858   } // loop on elements
6859
6860   if ( editor && editor != this )
6861     myLastCreatedElems.swap( editor->myLastCreatedElems );
6862
6863   PGroupIDs newGroupIDs;
6864
6865   if ( ( theMakeGroups && theCopy ) ||
6866        ( theMakeGroups && theTargetMesh ) )
6867     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6868
6869   return newGroupIDs;
6870 }
6871
6872 //================================================================================
6873 /*!
6874  * \brief Make an offset mesh from a source 2D mesh
6875  *  \param [in] theElements - source faces
6876  *  \param [in] theValue - offset value
6877  *  \param [out] theTgtMesh - a mesh to add offset elements to
6878  *  \param [in] theMakeGroups - to generate groups
6879  *  \return PGroupIDs - IDs of created groups. NULL means failure
6880  */
6881 //================================================================================
6882
6883 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6884                                                       const double       theValue,
6885                                                       SMESH_Mesh*        theTgtMesh,
6886                                                       const bool         theMakeGroups,
6887                                                       const bool         theCopyElements,
6888                                                       const bool         theFixSelfIntersection)
6889 {
6890   SMESHDS_Mesh*    meshDS = GetMeshDS();
6891   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6892   SMESH_MeshEditor tgtEditor( theTgtMesh );
6893
6894   SMDS_ElemIteratorPtr eIt;
6895   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6896   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6897
6898   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6899   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6900   std::unique_ptr< SMDS_Mesh > offsetMesh
6901     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6902                                    theFixSelfIntersection,
6903                                    new2OldFaces, new2OldNodes ));
6904   if ( offsetMesh->NbElements() == 0 )
6905     return PGroupIDs(); // MakeOffset() failed
6906
6907
6908   if ( theTgtMesh == myMesh && !theCopyElements )
6909   {
6910     // clear the source elements
6911     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6912     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6913     while ( eIt->more() )
6914       meshDS->RemoveFreeElement( eIt->next(), 0 );
6915   }
6916
6917   // offsetMesh->Modified();
6918   // offsetMesh->CompactMesh(); // make IDs start from 1
6919
6920   // source elements for each generated one
6921   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6922   srcElems.reserve( new2OldFaces.size() );
6923   srcNodes.reserve( new2OldNodes.size() );
6924
6925   ClearLastCreated();
6926   myLastCreatedElems.reserve( new2OldFaces.size() );
6927   myLastCreatedNodes.reserve( new2OldNodes.size() );
6928
6929   // copy offsetMesh to theTgtMesh
6930
6931   smIdType idShift = meshDS->MaxNodeID();
6932   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6933     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6934     {
6935
6936       if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6937       {
6938         const SMDS_MeshNode* n2 =
6939           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6940         myLastCreatedNodes.push_back( n2 );
6941         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6942       }
6943     }
6944
6945   ElemFeatures elemType;
6946   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6947     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6948     {
6949       elemType.Init( f );
6950       elemType.myNodes.clear();
6951       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6952       {
6953         const SMDS_MeshNode* n2 = nIt->next();
6954         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6955       }
6956       tgtEditor.AddElement( elemType.myNodes, elemType );
6957       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6958     }
6959
6960   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6961
6962   PGroupIDs newGroupIDs;
6963   if ( theMakeGroups )
6964     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6965   else
6966     newGroupIDs.reset( new std::list< int > );
6967
6968   return newGroupIDs;
6969 }
6970
6971 //=======================================================================
6972 /*!
6973  * \brief Create groups of elements made during transformation
6974  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6975  *  \param elemGens - elements making corresponding myLastCreatedElems
6976  *  \param postfix - to push_back to names of new groups
6977  *  \param targetMesh - mesh to create groups in
6978  *  \param topPresent - is there are "top" elements that are created by sweeping
6979  */
6980 //=======================================================================
6981
6982 SMESH_MeshEditor::PGroupIDs
6983 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6984                                  const SMESH_SequenceOfElemPtr& elemGens,
6985                                  const std::string&             postfix,
6986                                  SMESH_Mesh*                    targetMesh,
6987                                  const bool                     topPresent)
6988 {
6989   PGroupIDs newGroupIDs( new list<int> );
6990   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6991
6992   // Sort existing groups by types and collect their names
6993
6994   // containers to store an old group and generated new ones;
6995   // 1st new group is for result elems of different type than a source one;
6996   // 2nd new group is for same type result elems ("top" group at extrusion)
6997   using boost::tuple;
6998   using boost::make_tuple;
6999   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7000   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7001   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7002   // group names
7003   set< string > groupNames;
7004
7005   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7006   if ( !groupIt->more() ) return newGroupIDs;
7007
7008   int newGroupID = mesh->GetGroupIds().back()+1;
7009   while ( groupIt->more() )
7010   {
7011     SMESH_Group * group = groupIt->next();
7012     if ( !group ) continue;
7013     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7014     if ( !groupDS || groupDS->IsEmpty() ) continue;
7015     groupNames.insert    ( group->GetName() );
7016     groupDS->SetStoreName( group->GetName() );
7017     const SMDSAbs_ElementType type = groupDS->GetType();
7018     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7019     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7020     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7021     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7022   }
7023
7024   // Loop on nodes and elements to add them in new groups
7025
7026   vector< const SMDS_MeshElement* > resultElems;
7027   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7028   {
7029     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7030     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7031     if ( gens.size() != elems.size() )
7032       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7033
7034     // loop on created elements
7035     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
7036     {
7037       const SMDS_MeshElement* sourceElem = gens[ iElem ];
7038       if ( !sourceElem ) {
7039         MESSAGE("generateGroups(): NULL source element");
7040         continue;
7041       }
7042       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7043       if ( groupsOldNew.empty() ) { // no groups of this type at all
7044         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7045           ++iElem; // skip all elements made by sourceElem
7046         continue;
7047       }
7048       // collect all elements made by the iElem-th sourceElem
7049       resultElems.clear();
7050       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
7051         if ( resElem != sourceElem )
7052           resultElems.push_back( resElem );
7053       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7054         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
7055           if ( resElem != sourceElem )
7056             resultElems.push_back( resElem );
7057
7058       const SMDS_MeshElement* topElem = 0;
7059       if ( isNodes ) // there must be a top element
7060       {
7061         topElem = resultElems.back();
7062         resultElems.pop_back();
7063       }
7064       else
7065       {
7066         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7067         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7068           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7069           {
7070             topElem = *resElemIt;
7071             *resElemIt = 0; // erase *resElemIt
7072             break;
7073           }
7074       }
7075       // add resultElems to groups originted from ones the sourceElem belongs to
7076       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7077       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7078       {
7079         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7080         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7081         {
7082           // fill in a new group
7083           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7084           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7085           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7086             if ( *resElemIt )
7087               newGroup.Add( *resElemIt );
7088
7089           // fill a "top" group
7090           if ( topElem )
7091           {
7092             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7093             newTopGroup.Add( topElem );
7094           }
7095         }
7096       }
7097     } // loop on created elements
7098   }// loop on nodes and elements
7099
7100   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7101
7102   list<int> topGrouIds;
7103   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7104   {
7105     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7106     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7107                                       orderedOldNewGroups[i]->get<2>() };
7108     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7109     {
7110       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7111       if ( newGroupDS->IsEmpty() )
7112       {
7113         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7114       }
7115       else
7116       {
7117         // set group type
7118         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7119
7120         // make a name
7121         const bool isTop = ( topPresent &&
7122                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7123                              is2nd );
7124
7125         string name = oldGroupDS->GetStoreName();
7126         { // remove trailing whitespaces (issue 22599)
7127           size_t size = name.size();
7128           while ( size > 1 && isspace( name[ size-1 ]))
7129             --size;
7130           if ( size != name.size() )
7131           {
7132             name.resize( size );
7133             oldGroupDS->SetStoreName( name.c_str() );
7134           }
7135         }
7136         if ( !targetMesh ) {
7137           string suffix = ( isTop ? "top": postfix.c_str() );
7138           name += "_";
7139           name += suffix;
7140           int nb = 1;
7141           while ( !groupNames.insert( name ).second ) // name exists
7142             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7143         }
7144         else if ( isTop ) {
7145           name += "_top";
7146         }
7147         newGroupDS->SetStoreName( name.c_str() );
7148
7149         // make a SMESH_Groups
7150         mesh->AddGroup( newGroupDS );
7151         if ( isTop )
7152           topGrouIds.push_back( newGroupDS->GetID() );
7153         else
7154           newGroupIDs->push_back( newGroupDS->GetID() );
7155       }
7156     }
7157   }
7158   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7159
7160   return newGroupIDs;
7161 }
7162
7163 //================================================================================
7164 /*!
7165  *  * \brief Return list of group of nodes close to each other within theTolerance
7166  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7167  *  *        an Octree algorithm
7168  *  \param [in,out] theNodes - the nodes to treat
7169  *  \param [in]     theTolerance - the tolerance
7170  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7171  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7172  *         corner and medium nodes in separate groups
7173  */
7174 //================================================================================
7175
7176 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7177                                             const double         theTolerance,
7178                                             TListOfListOfNodes & theGroupsOfNodes,
7179                                             bool                 theSeparateCornersAndMedium)
7180 {
7181   ClearLastCreated();
7182
7183   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7184        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7185        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7186     theSeparateCornersAndMedium = false;
7187
7188   TIDSortedNodeSet& corners = theNodes;
7189   TIDSortedNodeSet  medium;
7190
7191   if ( theNodes.empty() ) // get all nodes in the mesh
7192   {
7193     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7194     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7195     if ( theSeparateCornersAndMedium )
7196       while ( nIt->more() )
7197       {
7198         const SMDS_MeshNode* n = nIt->next();
7199         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7200         nodeSet->insert( nodeSet->end(), n );
7201       }
7202     else
7203       while ( nIt->more() )
7204         theNodes.insert( theNodes.end(), nIt->next() );
7205   }
7206   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7207   {
7208     TIDSortedNodeSet::iterator nIt = corners.begin();
7209     while ( nIt != corners.end() )
7210       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7211       {
7212         medium.insert( medium.end(), *nIt );
7213         corners.erase( nIt++ );
7214       }
7215       else
7216       {
7217         ++nIt;
7218       }
7219   }
7220
7221   if ( !corners.empty() )
7222     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7223   if ( !medium.empty() )
7224     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7225 }
7226
7227 //=======================================================================
7228 //function : SimplifyFace
7229 //purpose  : split a chain of nodes into several closed chains
7230 //=======================================================================
7231
7232 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7233                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7234                                     vector<int>&                         quantities) const
7235 {
7236   int nbNodes = faceNodes.size();
7237   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7238     --nbNodes;
7239   if ( nbNodes < 3 )
7240     return 0;
7241   size_t prevNbQuant = quantities.size();
7242
7243   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7244   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7245   map< const SMDS_MeshNode*, int >::iterator nInd;
7246
7247   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7248   simpleNodes.push_back( faceNodes[0] );
7249   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7250   {
7251     if ( faceNodes[ iCur ] != simpleNodes.back() )
7252     {
7253       int index = simpleNodes.size();
7254       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7255       int prevIndex = nInd->second;
7256       if ( prevIndex < index )
7257       {
7258         // a sub-loop found
7259         int loopLen = index - prevIndex;
7260         if ( loopLen > 2 )
7261         {
7262           // store the sub-loop
7263           quantities.push_back( loopLen );
7264           for ( int i = prevIndex; i < index; i++ )
7265             poly_nodes.push_back( simpleNodes[ i ]);
7266         }
7267         simpleNodes.resize( prevIndex+1 );
7268       }
7269       else
7270       {
7271         simpleNodes.push_back( faceNodes[ iCur ]);
7272       }
7273     }
7274   }
7275
7276   if ( simpleNodes.size() > 2 )
7277   {
7278     quantities.push_back( simpleNodes.size() );
7279     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7280   }
7281
7282   return quantities.size() - prevNbQuant;
7283 }
7284
7285 //=======================================================================
7286 //function : MergeNodes
7287 //purpose  : In each group, the cdr of nodes are substituted by the first one
7288 //           in all elements.
7289 //=======================================================================
7290
7291 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7292                                    const bool           theAvoidMakingHoles)
7293 {
7294   ClearLastCreated();
7295
7296   SMESHDS_Mesh* mesh = GetMeshDS();
7297
7298   TNodeNodeMap nodeNodeMap; // node to replace - new node
7299   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7300   list< smIdType > rmElemIds, rmNodeIds;
7301   vector< ElemFeatures > newElemDefs;
7302
7303   // Fill nodeNodeMap and elems
7304
7305   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7306   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7307   {
7308     list<const SMDS_MeshNode*>& nodes = *grIt;
7309     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7310     const SMDS_MeshNode* nToKeep = *nIt;
7311     for ( ++nIt; nIt != nodes.end(); nIt++ )
7312     {
7313       const SMDS_MeshNode* nToRemove = *nIt;
7314       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7315       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7316       while ( invElemIt->more() ) {
7317         const SMDS_MeshElement* elem = invElemIt->next();
7318         elems.insert(elem);
7319       }
7320     }
7321   }
7322
7323   // Apply recursive replacements (BUG 0020185)
7324   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7325   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7326   {
7327     const SMDS_MeshNode* nToKeep = nnIt->second;
7328     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7329     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7330     {
7331       nToKeep = nnIt_i->second;
7332       nnIt->second = nToKeep;
7333       nnIt_i = nodeNodeMap.find( nToKeep );
7334     }
7335   }
7336
7337   if ( theAvoidMakingHoles )
7338   {
7339     // find elements whose topology changes
7340
7341     vector<const SMDS_MeshElement*> pbElems;
7342     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7343     for ( ; eIt != elems.end(); ++eIt )
7344     {
7345       const SMDS_MeshElement* elem = *eIt;
7346       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7347       while ( itN->more() )
7348       {
7349         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7350         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7351         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7352         {
7353           // several nodes of elem stick
7354           pbElems.push_back( elem );
7355           break;
7356         }
7357       }
7358     }
7359     // exclude from merge nodes causing spoiling element
7360     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7361     {
7362       bool nodesExcluded = false;
7363       for ( size_t i = 0; i < pbElems.size(); ++i )
7364       {
7365         size_t prevNbMergeNodes = nodeNodeMap.size();
7366         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7367              prevNbMergeNodes < nodeNodeMap.size() )
7368           nodesExcluded = true;
7369       }
7370       if ( !nodesExcluded )
7371         break;
7372     }
7373   }
7374
7375   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7376   {
7377     const SMDS_MeshNode* nToRemove = nnIt->first;
7378     const SMDS_MeshNode* nToKeep   = nnIt->second;
7379     if ( nToRemove != nToKeep )
7380     {
7381       rmNodeIds.push_back( nToRemove->GetID() );
7382       AddToSameGroups( nToKeep, nToRemove, mesh );
7383       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7384       // w/o creating node in place of merged ones.
7385       SMDS_PositionPtr pos = nToRemove->GetPosition();
7386       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7387         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7388           sm->SetIsAlwaysComputed( true );
7389     }
7390   }
7391
7392   // Change element nodes or remove an element
7393
7394   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7395   for ( ; eIt != elems.end(); eIt++ )
7396   {
7397     const SMDS_MeshElement* elem = *eIt;
7398     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7399     bool                 marked = elem->isMarked();
7400
7401     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7402     if ( !keepElem )
7403       rmElemIds.push_back( elem->GetID() );
7404
7405     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7406     {
7407       bool elemChanged = false;
7408       if ( i == 0 )
7409       {
7410         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7411           elemChanged = mesh->ChangePolyhedronNodes( elem,
7412                                                      newElemDefs[i].myNodes,
7413                                                      newElemDefs[i].myPolyhedQuantities );
7414         else
7415           elemChanged = mesh->ChangeElementNodes( elem,
7416                                                   & newElemDefs[i].myNodes[0],
7417                                                   newElemDefs[i].myNodes.size() );
7418       }
7419       if ( i > 0 || !elemChanged )
7420       {
7421         if ( i == 0 )
7422         {
7423           newElemDefs[i].SetID( elem->GetID() );
7424           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7425           if ( !keepElem ) rmElemIds.pop_back();
7426         }
7427         else
7428         {
7429           newElemDefs[i].SetID( -1 );
7430         }
7431         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7432         if ( sm && newElem )
7433           sm->AddElement( newElem );
7434         if ( elem != newElem )
7435           ReplaceElemInGroups( elem, newElem, mesh );
7436         if ( marked && newElem )
7437           newElem->setIsMarked( true );
7438       }
7439     }
7440   }
7441
7442   // Remove bad elements, then equal nodes (order important)
7443   Remove( rmElemIds, /*isNodes=*/false );
7444   Remove( rmNodeIds, /*isNodes=*/true );
7445
7446   return;
7447 }
7448
7449 //=======================================================================
7450 //function : applyMerge
7451 //purpose  : Compute new connectivity of an element after merging nodes
7452 //  \param [in] elems - the element
7453 //  \param [out] newElemDefs - definition(s) of result element(s)
7454 //  \param [inout] nodeNodeMap - nodes to merge
7455 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7456 //              after merging (but not degenerated), removes nodes causing
7457 //              the invalidity from \a nodeNodeMap.
7458 //  \return bool - true if the element should be removed
7459 //=======================================================================
7460
7461 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7462                                    vector< ElemFeatures >& newElemDefs,
7463                                    TNodeNodeMap&           nodeNodeMap,
7464                                    const bool              avoidMakingHoles )
7465 {
7466   bool toRemove = false; // to remove elem
7467   int nbResElems = 1;    // nb new elements
7468
7469   newElemDefs.resize(nbResElems);
7470   newElemDefs[0].Init( elem );
7471   newElemDefs[0].myNodes.clear();
7472
7473   set<const SMDS_MeshNode*> nodeSet;
7474   vector< const SMDS_MeshNode*>   curNodes;
7475   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7476   vector<int> iRepl;
7477
7478   const        int  nbNodes = elem->NbNodes();
7479   SMDSAbs_EntityType entity = elem->GetEntityType();
7480
7481   curNodes.resize( nbNodes );
7482   uniqueNodes.resize( nbNodes );
7483   iRepl.resize( nbNodes );
7484   int iUnique = 0, iCur = 0, nbRepl = 0;
7485
7486   // Get new seq of nodes
7487
7488   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7489   while ( itN->more() )
7490   {
7491     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7492
7493     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7494     if ( nnIt != nodeNodeMap.end() ) {
7495       n = (*nnIt).second;
7496     }
7497     curNodes[ iCur ] = n;
7498     bool isUnique = nodeSet.insert( n ).second;
7499     if ( isUnique )
7500       uniqueNodes[ iUnique++ ] = n;
7501     else
7502       iRepl[ nbRepl++ ] = iCur;
7503     iCur++;
7504   }
7505
7506   // Analyse element topology after replacement
7507
7508   int nbUniqueNodes = nodeSet.size();
7509   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7510   {
7511     toRemove = true;
7512     nbResElems = 0;
7513
7514     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7515     {
7516       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7517       int nbCorners = nbNodes / 2;
7518       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7519       {
7520         int iNext = ( iCur + 1 ) % nbCorners;
7521         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7522         {
7523           int iMedium = iCur + nbCorners;
7524           vector< const SMDS_MeshNode* >::iterator i =
7525             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7526                        uniqueNodes.end(),
7527                        curNodes[ iMedium ]);
7528           if ( i != uniqueNodes.end() )
7529           {
7530             --nbUniqueNodes;
7531             for ( ; i+1 != uniqueNodes.end(); ++i )
7532               *i = *(i+1);
7533           }
7534         }
7535       }
7536     }
7537
7538     switch ( entity )
7539     {
7540     case SMDSEntity_Polygon:
7541     case SMDSEntity_Quad_Polygon: // Polygon
7542     {
7543       ElemFeatures* elemType = & newElemDefs[0];
7544       const bool isQuad = elemType->myIsQuad;
7545       if ( isQuad )
7546         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7547           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7548
7549       // a polygon can divide into several elements
7550       vector<const SMDS_MeshNode *> polygons_nodes;
7551       vector<int> quantities;
7552       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7553       newElemDefs.resize( nbResElems );
7554       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7555       {
7556         ElemFeatures* elemType = & newElemDefs[iface];
7557         if ( iface ) elemType->Init( elem );
7558
7559         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7560         int nbNewNodes = quantities[iface];
7561         face_nodes.assign( polygons_nodes.begin() + inode,
7562                            polygons_nodes.begin() + inode + nbNewNodes );
7563         inode += nbNewNodes;
7564         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7565         {
7566           bool isValid = ( nbNewNodes % 2 == 0 );
7567           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7568             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7569           elemType->SetQuad( isValid );
7570           if ( isValid ) // put medium nodes after corners
7571             SMDS_MeshCell::applyInterlaceRev
7572               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7573                                                     nbNewNodes ), face_nodes );
7574         }
7575         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7576       }
7577       nbUniqueNodes = newElemDefs[0].myNodes.size();
7578       break;
7579     } // Polygon
7580
7581     case SMDSEntity_Polyhedra: // Polyhedral volume
7582     {
7583       if ( nbUniqueNodes >= 4 )
7584       {
7585         // each face has to be analyzed in order to check volume validity
7586         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7587         {
7588           toRemove = false;
7589           int nbFaces = aPolyedre->NbFaces();
7590
7591           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7592           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7593           vector<const SMDS_MeshNode *>  faceNodes;
7594           poly_nodes.clear();
7595           quantities.clear();
7596
7597           for (int iface = 1; iface <= nbFaces; iface++)
7598           {
7599             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7600             faceNodes.resize( nbFaceNodes );
7601             for (int inode = 1; inode <= nbFaceNodes; inode++)
7602             {
7603               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7604               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7605               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7606                 faceNode = (*nnIt).second;
7607               faceNodes[inode - 1] = faceNode;
7608             }
7609             SimplifyFace(faceNodes, poly_nodes, quantities);
7610           }
7611
7612           if ( quantities.size() > 3 )
7613           {
7614             // TODO: remove coincident faces
7615             nbResElems = 1;
7616             nbUniqueNodes = newElemDefs[0].myNodes.size();
7617           }
7618         }
7619       }
7620     }
7621     break;
7622
7623     // Regular elements
7624     // TODO not all the possible cases are solved. Find something more generic?
7625     case SMDSEntity_Edge: //////// EDGE
7626     case SMDSEntity_Triangle: //// TRIANGLE
7627     case SMDSEntity_Quad_Triangle:
7628     case SMDSEntity_Tetra:
7629     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7630     {
7631       break;
7632     }
7633     case SMDSEntity_Quad_Edge:
7634     {
7635       break;
7636     }
7637     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7638     {
7639       if ( nbUniqueNodes < 3 )
7640         toRemove = true;
7641       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7642         toRemove = true; // opposite nodes stick
7643       else
7644         toRemove = false;
7645       break;
7646     }
7647     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7648     {
7649       //   1    5    2
7650       //    +---+---+
7651       //    |       |
7652       //   4+       +6
7653       //    |       |
7654       //    +---+---+
7655       //   0    7    3
7656       if ( nbUniqueNodes == 6 &&
7657            iRepl[0] < 4       &&
7658            ( nbRepl == 1 || iRepl[1] >= 4 ))
7659       {
7660         toRemove = false;
7661       }
7662       break;
7663     }
7664     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7665     {
7666       //   1    5    2
7667       //    +---+---+
7668       //    |       |
7669       //   4+  8+   +6
7670       //    |       |
7671       //    +---+---+
7672       //   0    7    3
7673       if ( nbUniqueNodes == 7 &&
7674            iRepl[0] < 4       &&
7675            ( nbRepl == 1 || iRepl[1] != 8 ))
7676       {
7677         toRemove = false;
7678       }
7679       break;
7680     }
7681     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7682     {
7683       if ( nbUniqueNodes == 4 ) {
7684         // ---------------------------------> tetrahedron
7685         if ( curNodes[3] == curNodes[4] &&
7686              curNodes[3] == curNodes[5] ) {
7687           // top nodes stick
7688           toRemove = false;
7689         }
7690         else if ( curNodes[0] == curNodes[1] &&
7691                   curNodes[0] == curNodes[2] ) {
7692           // bottom nodes stick: set a top before
7693           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7694           uniqueNodes[ 0 ] = curNodes [ 5 ];
7695           uniqueNodes[ 1 ] = curNodes [ 4 ];
7696           uniqueNodes[ 2 ] = curNodes [ 3 ];
7697           toRemove = false;
7698         }
7699         else if (( curNodes[0] == curNodes[3] ) +
7700                  ( curNodes[1] == curNodes[4] ) +
7701                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7702           // a lateral face turns into a line
7703           toRemove = false;
7704         }
7705       }
7706       else if ( nbUniqueNodes == 5 ) {
7707         // PENTAHEDRON --------------------> pyramid
7708         if ( curNodes[0] == curNodes[3] )
7709         {
7710           uniqueNodes[ 0 ] = curNodes[ 1 ];
7711           uniqueNodes[ 1 ] = curNodes[ 4 ];
7712           uniqueNodes[ 2 ] = curNodes[ 5 ];
7713           uniqueNodes[ 3 ] = curNodes[ 2 ];
7714           uniqueNodes[ 4 ] = curNodes[ 0 ];
7715           toRemove = false;
7716         }
7717         if ( curNodes[1] == curNodes[4] )
7718         {
7719           uniqueNodes[ 0 ] = curNodes[ 0 ];
7720           uniqueNodes[ 1 ] = curNodes[ 2 ];
7721           uniqueNodes[ 2 ] = curNodes[ 5 ];
7722           uniqueNodes[ 3 ] = curNodes[ 3 ];
7723           uniqueNodes[ 4 ] = curNodes[ 1 ];
7724           toRemove = false;
7725         }
7726         if ( curNodes[2] == curNodes[5] )
7727         {
7728           uniqueNodes[ 0 ] = curNodes[ 0 ];
7729           uniqueNodes[ 1 ] = curNodes[ 3 ];
7730           uniqueNodes[ 2 ] = curNodes[ 4 ];
7731           uniqueNodes[ 3 ] = curNodes[ 1 ];
7732           uniqueNodes[ 4 ] = curNodes[ 2 ];
7733           toRemove = false;
7734         }
7735       }
7736       break;
7737     }
7738     case SMDSEntity_Hexa:
7739     {
7740       //////////////////////////////////// HEXAHEDRON
7741       SMDS_VolumeTool hexa (elem);
7742       hexa.SetExternalNormal();
7743       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7744         //////////////////////// HEX ---> tetrahedron
7745         for ( int iFace = 0; iFace < 6; iFace++ ) {
7746           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7747           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7748               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7749               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7750             // one face turns into a point ...
7751             int  pickInd = ind[ 0 ];
7752             int iOppFace = hexa.GetOppFaceIndex( iFace );
7753             ind = hexa.GetFaceNodesIndices( iOppFace );
7754             int nbStick = 0;
7755             uniqueNodes.clear();
7756             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7757               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7758                 nbStick++;
7759               else
7760                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7761             }
7762             if ( nbStick == 1 ) {
7763               // ... and the opposite one - into a triangle.
7764               // set a top node
7765               uniqueNodes.push_back( curNodes[ pickInd ]);
7766               toRemove = false;
7767             }
7768             break;
7769           }
7770         }
7771       }
7772       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7773         //////////////////////// HEX ---> prism
7774         int nbTria = 0, iTria[3];
7775         const int *ind; // indices of face nodes
7776         // look for triangular faces
7777         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7778           ind = hexa.GetFaceNodesIndices( iFace );
7779           TIDSortedNodeSet faceNodes;
7780           for ( iCur = 0; iCur < 4; iCur++ )
7781             faceNodes.insert( curNodes[ind[iCur]] );
7782           if ( faceNodes.size() == 3 )
7783             iTria[ nbTria++ ] = iFace;
7784         }
7785         // check if triangles are opposite
7786         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7787         {
7788           // set nodes of the bottom triangle
7789           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7790           vector<int> indB;
7791           for ( iCur = 0; iCur < 4; iCur++ )
7792             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7793               indB.push_back( ind[iCur] );
7794           if ( !hexa.IsForward() )
7795             std::swap( indB[0], indB[2] );
7796           for ( iCur = 0; iCur < 3; iCur++ )
7797             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7798           // set nodes of the top triangle
7799           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7800           for ( iCur = 0; iCur < 3; ++iCur )
7801             for ( int j = 0; j < 4; ++j )
7802               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7803               {
7804                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7805                 break;
7806               }
7807           toRemove = false;
7808           break;
7809         }
7810       }
7811       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7812         //////////////////// HEXAHEDRON ---> pyramid
7813         for ( int iFace = 0; iFace < 6; iFace++ ) {
7814           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7815           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7816               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7817               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7818             // one face turns into a point ...
7819             int iOppFace = hexa.GetOppFaceIndex( iFace );
7820             ind = hexa.GetFaceNodesIndices( iOppFace );
7821             uniqueNodes.clear();
7822             for ( iCur = 0; iCur < 4; iCur++ ) {
7823               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7824                 break;
7825               else
7826                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7827             }
7828             if ( uniqueNodes.size() == 4 ) {
7829               // ... and the opposite one is a quadrangle
7830               // set a top node
7831               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7832               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7833               toRemove = false;
7834             }
7835             break;
7836           }
7837         }
7838       }
7839
7840       if ( toRemove && nbUniqueNodes > 4 ) {
7841         ////////////////// HEXAHEDRON ---> polyhedron
7842         hexa.SetExternalNormal();
7843         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7844         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7845         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7846         quantities.reserve( 6 );     quantities.clear();
7847         for ( int iFace = 0; iFace < 6; iFace++ )
7848         {
7849           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7850           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7851                curNodes[ind[1]] == curNodes[ind[3]] )
7852           {
7853             quantities.clear();
7854             break; // opposite nodes stick
7855           }
7856           nodeSet.clear();
7857           for ( iCur = 0; iCur < 4; iCur++ )
7858           {
7859             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7860               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7861           }
7862           if ( nodeSet.size() < 3 )
7863             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7864           else
7865             quantities.push_back( nodeSet.size() );
7866         }
7867         if ( quantities.size() >= 4 )
7868         {
7869           nbResElems = 1;
7870           nbUniqueNodes = poly_nodes.size();
7871           newElemDefs[0].SetPoly(true);
7872         }
7873       }
7874       break;
7875     } // case HEXAHEDRON
7876
7877     default:
7878       toRemove = true;
7879
7880     } // switch ( entity )
7881
7882     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7883     {
7884       // erase from nodeNodeMap nodes whose merge spoils elem
7885       vector< const SMDS_MeshNode* > noMergeNodes;
7886       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7887       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7888         nodeNodeMap.erase( noMergeNodes[i] );
7889     }
7890     
7891   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7892
7893   uniqueNodes.resize( nbUniqueNodes );
7894
7895   if ( !toRemove && nbResElems == 0 )
7896     nbResElems = 1;
7897
7898   newElemDefs.resize( nbResElems );
7899
7900   return !toRemove;
7901 }
7902
7903
7904 // ========================================================
7905 // class   : ComparableElement
7906 // purpose : allow comparing elements basing on their nodes
7907 // ========================================================
7908
7909 class ComparableElement : public boost::container::flat_set< smIdType >
7910 {
7911   typedef boost::container::flat_set< smIdType >  int_set;
7912
7913   const SMDS_MeshElement* myElem;
7914   smIdType                mySumID;
7915   mutable int             myGroupID;
7916
7917 public:
7918
7919   ComparableElement( const SMDS_MeshElement* theElem ):
7920     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7921   {
7922     this->reserve( theElem->NbNodes() );
7923     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7924     {
7925       smIdType id = nodeIt->next()->GetID();
7926       mySumID += id;
7927       this->insert( id );
7928     }
7929   }
7930
7931   const SMDS_MeshElement* GetElem() const { return myElem; }
7932
7933   int& GroupID() const { return myGroupID; }
7934   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7935
7936   ComparableElement( const ComparableElement& theSource ) // move copy
7937     : int_set()
7938   {
7939     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7940     (int_set&) (*this ) = std::move( src );
7941     myElem    = src.myElem;
7942     mySumID   = src.mySumID;
7943     myGroupID = src.myGroupID;
7944   }
7945
7946   static int HashCode(const ComparableElement& se, int limit )
7947   {
7948     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7949   }
7950   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7951   {
7952     return ( se1 == se2 );
7953   }
7954
7955 };
7956
7957 //=======================================================================
7958 //function : FindEqualElements
7959 //purpose  : Return list of group of elements built on the same nodes.
7960 //           Search among theElements or in the whole mesh if theElements is empty
7961 //=======================================================================
7962
7963 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7964                                           TListOfListOfElementsID & theGroupsOfElementsID )
7965 {
7966   ClearLastCreated();
7967
7968   SMDS_ElemIteratorPtr elemIt;
7969   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7970   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7971
7972   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7973   typedef std::list<smIdType>                                     TGroupOfElems;
7974   TMapOfElements               mapOfElements;
7975   std::vector< TGroupOfElems > arrayOfGroups;
7976   TGroupOfElems                groupOfElems;
7977
7978   while ( elemIt->more() )
7979   {
7980     const SMDS_MeshElement* curElem = elemIt->next();
7981     if ( curElem->IsNull() )
7982       continue;
7983     ComparableElement      compElem = curElem;
7984     // check uniqueness
7985     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7986     if ( elemInSet.GetElem() != curElem ) // coincident elem
7987     {
7988       int& iG = elemInSet.GroupID();
7989       if ( iG < 0 )
7990       {
7991         iG = arrayOfGroups.size();
7992         arrayOfGroups.push_back( groupOfElems );
7993         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7994       }
7995       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7996     }
7997   }
7998
7999   groupOfElems.clear();
8000   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8001   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8002   {
8003     if ( groupIt->size() > 1 ) {
8004       //groupOfElems.sort(); -- theElements are sorted already
8005       theGroupsOfElementsID.emplace_back( *groupIt );
8006     }
8007   }
8008 }
8009
8010 //=======================================================================
8011 //function : MergeElements
8012 //purpose  : In each given group, substitute all elements by the first one.
8013 //=======================================================================
8014
8015 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8016 {
8017   ClearLastCreated();
8018
8019   typedef list<smIdType> TListOfIDs;
8020   TListOfIDs rmElemIds; // IDs of elems to remove
8021
8022   SMESHDS_Mesh* aMesh = GetMeshDS();
8023
8024   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8025   while ( groupsIt != theGroupsOfElementsID.end() ) {
8026     TListOfIDs& aGroupOfElemID = *groupsIt;
8027     aGroupOfElemID.sort();
8028     int elemIDToKeep = aGroupOfElemID.front();
8029     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8030     aGroupOfElemID.pop_front();
8031     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8032     while ( idIt != aGroupOfElemID.end() ) {
8033       int elemIDToRemove = *idIt;
8034       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8035       // add the kept element in groups of removed one (PAL15188)
8036       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8037       rmElemIds.push_back( elemIDToRemove );
8038       ++idIt;
8039     }
8040     ++groupsIt;
8041   }
8042
8043   Remove( rmElemIds, false );
8044 }
8045
8046 //=======================================================================
8047 //function : MergeEqualElements
8048 //purpose  : Remove all but one of elements built on the same nodes.
8049 //=======================================================================
8050
8051 void SMESH_MeshEditor::MergeEqualElements()
8052 {
8053   TIDSortedElemSet aMeshElements; /* empty input ==
8054                                      to merge equal elements in the whole mesh */
8055   TListOfListOfElementsID aGroupsOfElementsID;
8056   FindEqualElements( aMeshElements, aGroupsOfElementsID );
8057   MergeElements( aGroupsOfElementsID );
8058 }
8059
8060 //=======================================================================
8061 //function : findAdjacentFace
8062 //purpose  :
8063 //=======================================================================
8064
8065 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8066                                                 const SMDS_MeshNode* n2,
8067                                                 const SMDS_MeshElement* elem)
8068 {
8069   TIDSortedElemSet elemSet, avoidSet;
8070   if ( elem )
8071     avoidSet.insert ( elem );
8072   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8073 }
8074
8075 //=======================================================================
8076 //function : findSegment
8077 //purpose  : Return a mesh segment by two nodes one of which can be medium
8078 //=======================================================================
8079
8080 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8081                                            const SMDS_MeshNode* n2)
8082 {
8083   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8084   while ( it->more() )
8085   {
8086     const SMDS_MeshElement* seg = it->next();
8087     if ( seg->GetNodeIndex( n2 ) >= 0 )
8088       return seg;
8089   }
8090   return 0;
8091 }
8092
8093 //=======================================================================
8094 //function : FindFreeBorder
8095 //purpose  :
8096 //=======================================================================
8097
8098 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8099
8100 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8101                                        const SMDS_MeshNode*             theSecondNode,
8102                                        const SMDS_MeshNode*             theLastNode,
8103                                        list< const SMDS_MeshNode* > &   theNodes,
8104                                        list< const SMDS_MeshElement* >& theFaces)
8105 {
8106   if ( !theFirstNode || !theSecondNode )
8107     return false;
8108   // find border face between theFirstNode and theSecondNode
8109   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8110   if ( !curElem )
8111     return false;
8112
8113   theFaces.push_back( curElem );
8114   theNodes.push_back( theFirstNode );
8115   theNodes.push_back( theSecondNode );
8116
8117   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8118   //TIDSortedElemSet foundElems;
8119   bool needTheLast = ( theLastNode != 0 );
8120
8121   vector<const SMDS_MeshNode*> nodes;
8122   
8123   while ( nStart != theLastNode ) {
8124     if ( nStart == theFirstNode )
8125       return !needTheLast;
8126
8127     // find all free border faces sharing nStart
8128
8129     list< const SMDS_MeshElement* > curElemList;
8130     list< const SMDS_MeshNode* >    nStartList;
8131     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8132     while ( invElemIt->more() ) {
8133       const SMDS_MeshElement* e = invElemIt->next();
8134       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8135       {
8136         // get nodes
8137         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8138                       SMDS_MeshElement::iterator() );
8139         nodes.push_back( nodes[ 0 ]);
8140
8141         // check 2 links
8142         int iNode = 0, nbNodes = nodes.size() - 1;
8143         for ( iNode = 0; iNode < nbNodes; iNode++ )
8144           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8145                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8146               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8147           {
8148             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8149             curElemList.push_back( e );
8150           }
8151       }
8152     }
8153     // analyse the found
8154
8155     int nbNewBorders = curElemList.size();
8156     if ( nbNewBorders == 0 ) {
8157       // no free border furthermore
8158       return !needTheLast;
8159     }
8160     else if ( nbNewBorders == 1 ) {
8161       // one more element found
8162       nIgnore = nStart;
8163       nStart = nStartList.front();
8164       curElem = curElemList.front();
8165       theFaces.push_back( curElem );
8166       theNodes.push_back( nStart );
8167     }
8168     else {
8169       // several continuations found
8170       list< const SMDS_MeshElement* >::iterator curElemIt;
8171       list< const SMDS_MeshNode* >::iterator nStartIt;
8172       // check if one of them reached the last node
8173       if ( needTheLast ) {
8174         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8175              curElemIt!= curElemList.end();
8176              curElemIt++, nStartIt++ )
8177           if ( *nStartIt == theLastNode ) {
8178             theFaces.push_back( *curElemIt );
8179             theNodes.push_back( *nStartIt );
8180             return true;
8181           }
8182       }
8183       // find the best free border by the continuations
8184       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8185       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8186       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8187            curElemIt!= curElemList.end();
8188            curElemIt++, nStartIt++ )
8189       {
8190         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8191         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8192         // find one more free border
8193         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8194           cNL->clear();
8195           cFL->clear();
8196         }
8197         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8198           // choice: clear a worse one
8199           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8200           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8201           contNodes[ iWorse ].clear();
8202           contFaces[ iWorse ].clear();
8203         }
8204       }
8205       if ( contNodes[0].empty() && contNodes[1].empty() )
8206         return false;
8207
8208       // push_back the best free border
8209       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8210       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8211       //theNodes.pop_back(); // remove nIgnore
8212       theNodes.pop_back(); // remove nStart
8213       //theFaces.pop_back(); // remove curElem
8214       theNodes.splice( theNodes.end(), *cNL );
8215       theFaces.splice( theFaces.end(), *cFL );
8216       return true;
8217
8218     } // several continuations found
8219   } // while ( nStart != theLastNode )
8220
8221   return true;
8222 }
8223
8224 //=======================================================================
8225 //function : CheckFreeBorderNodes
8226 //purpose  : Return true if the tree nodes are on a free border
8227 //=======================================================================
8228
8229 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8230                                             const SMDS_MeshNode* theNode2,
8231                                             const SMDS_MeshNode* theNode3)
8232 {
8233   list< const SMDS_MeshNode* > nodes;
8234   list< const SMDS_MeshElement* > faces;
8235   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8236 }
8237
8238 //=======================================================================
8239 //function : SewFreeBorder
8240 //purpose  :
8241 //warning  : for border-to-side sewing theSideSecondNode is considered as
8242 //           the last side node and theSideThirdNode is not used
8243 //=======================================================================
8244
8245 SMESH_MeshEditor::Sew_Error
8246 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8247                                  const SMDS_MeshNode* theBordSecondNode,
8248                                  const SMDS_MeshNode* theBordLastNode,
8249                                  const SMDS_MeshNode* theSideFirstNode,
8250                                  const SMDS_MeshNode* theSideSecondNode,
8251                                  const SMDS_MeshNode* theSideThirdNode,
8252                                  const bool           theSideIsFreeBorder,
8253                                  const bool           toCreatePolygons,
8254                                  const bool           toCreatePolyedrs)
8255 {
8256   ClearLastCreated();
8257
8258   Sew_Error aResult = SEW_OK;
8259
8260   // ====================================
8261   //    find side nodes and elements
8262   // ====================================
8263
8264   list< const SMDS_MeshNode* >    nSide[ 2 ];
8265   list< const SMDS_MeshElement* > eSide[ 2 ];
8266   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8267   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8268
8269   // Free border 1
8270   // --------------
8271   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8272                       nSide[0], eSide[0])) {
8273     MESSAGE(" Free Border 1 not found " );
8274     aResult = SEW_BORDER1_NOT_FOUND;
8275   }
8276   if (theSideIsFreeBorder) {
8277     // Free border 2
8278     // --------------
8279     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8280                         nSide[1], eSide[1])) {
8281       MESSAGE(" Free Border 2 not found " );
8282       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8283     }
8284   }
8285   if ( aResult != SEW_OK )
8286     return aResult;
8287
8288   if (!theSideIsFreeBorder) {
8289     // Side 2
8290     // --------------
8291
8292     // -------------------------------------------------------------------------
8293     // Algo:
8294     // 1. If nodes to merge are not coincident, move nodes of the free border
8295     //    from the coord sys defined by the direction from the first to last
8296     //    nodes of the border to the correspondent sys of the side 2
8297     // 2. On the side 2, find the links most co-directed with the correspondent
8298     //    links of the free border
8299     // -------------------------------------------------------------------------
8300
8301     // 1. Since sewing may break if there are volumes to split on the side 2,
8302     //    we won't move nodes but just compute new coordinates for them
8303     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8304     TNodeXYZMap nBordXYZ;
8305     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8306     list< const SMDS_MeshNode* >::iterator nBordIt;
8307
8308     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8309     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8310     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8311     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8312     double tol2 = 1.e-8;
8313     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8314     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8315       // Need node movement.
8316
8317       // find X and Z axes to create trsf
8318       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8319       gp_Vec X = Zs ^ Zb;
8320       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8321         // Zb || Zs
8322         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8323
8324       // coord systems
8325       gp_Ax3 toBordAx( Pb1, Zb, X );
8326       gp_Ax3 fromSideAx( Ps1, Zs, X );
8327       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8328       // set trsf
8329       gp_Trsf toBordSys, fromSide2Sys;
8330       toBordSys.SetTransformation( toBordAx );
8331       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8332       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8333
8334       // move
8335       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8336         const SMDS_MeshNode* n = *nBordIt;
8337         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8338         toBordSys.Transforms( xyz );
8339         fromSide2Sys.Transforms( xyz );
8340         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8341       }
8342     }
8343     else {
8344       // just insert nodes XYZ in the nBordXYZ map
8345       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8346         const SMDS_MeshNode* n = *nBordIt;
8347         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8348       }
8349     }
8350
8351     // 2. On the side 2, find the links most co-directed with the correspondent
8352     //    links of the free border
8353
8354     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8355     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8356     sideNodes.push_back( theSideFirstNode );
8357
8358     bool hasVolumes = false;
8359     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8360     set<long> foundSideLinkIDs, checkedLinkIDs;
8361     SMDS_VolumeTool volume;
8362     //const SMDS_MeshNode* faceNodes[ 4 ];
8363
8364     const SMDS_MeshNode*    sideNode;
8365     const SMDS_MeshElement* sideElem  = 0;
8366     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8367     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8368     nBordIt = bordNodes.begin();
8369     nBordIt++;
8370     // border node position and border link direction to compare with
8371     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8372     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8373     // choose next side node by link direction or by closeness to
8374     // the current border node:
8375     bool searchByDir = ( *nBordIt != theBordLastNode );
8376     do {
8377       // find the next node on the Side 2
8378       sideNode = 0;
8379       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8380       long linkID;
8381       checkedLinkIDs.clear();
8382       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8383
8384       // loop on inverse elements of current node (prevSideNode) on the Side 2
8385       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8386       while ( invElemIt->more() )
8387       {
8388         const SMDS_MeshElement* elem = invElemIt->next();
8389         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8390         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8391         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8392         bool isVolume = volume.Set( elem );
8393         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8394         if ( isVolume ) // --volume
8395           hasVolumes = true;
8396         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8397           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8398           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8399           while ( nIt->more() ) {
8400             nodes[ iNode ] = cast2Node( nIt->next() );
8401             if ( nodes[ iNode++ ] == prevSideNode )
8402               iPrevNode = iNode - 1;
8403           }
8404           // there are 2 links to check
8405           nbNodes = 2;
8406         }
8407         else // --edge
8408           continue;
8409         // loop on links, to be precise, on the second node of links
8410         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8411           const SMDS_MeshNode* n = nodes[ iNode ];
8412           if ( isVolume ) {
8413             if ( !volume.IsLinked( n, prevSideNode ))
8414               continue;
8415           }
8416           else {
8417             if ( iNode ) // a node before prevSideNode
8418               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8419             else         // a node after prevSideNode
8420               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8421           }
8422           // check if this link was already used
8423           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8424           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8425           if (!isJustChecked &&
8426               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8427           {
8428             // test a link geometrically
8429             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8430             bool linkIsBetter = false;
8431             double dot = 0.0, dist = 0.0;
8432             if ( searchByDir ) { // choose most co-directed link
8433               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8434               linkIsBetter = ( dot > maxDot );
8435             }
8436             else { // choose link with the node closest to bordPos
8437               dist = ( nextXYZ - bordPos ).SquareModulus();
8438               linkIsBetter = ( dist < minDist );
8439             }
8440             if ( linkIsBetter ) {
8441               maxDot = dot;
8442               minDist = dist;
8443               linkID = iLink;
8444               sideNode = n;
8445               sideElem = elem;
8446             }
8447           }
8448         }
8449       } // loop on inverse elements of prevSideNode
8450
8451       if ( !sideNode ) {
8452         MESSAGE(" Can't find path by links of the Side 2 ");
8453         return SEW_BAD_SIDE_NODES;
8454       }
8455       sideNodes.push_back( sideNode );
8456       sideElems.push_back( sideElem );
8457       foundSideLinkIDs.insert ( linkID );
8458       prevSideNode = sideNode;
8459
8460       if ( *nBordIt == theBordLastNode )
8461         searchByDir = false;
8462       else {
8463         // find the next border link to compare with
8464         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8465         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8466         // move to next border node if sideNode is before forward border node (bordPos)
8467         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8468           prevBordNode = *nBordIt;
8469           nBordIt++;
8470           bordPos = nBordXYZ[ *nBordIt ];
8471           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8472           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8473         }
8474       }
8475     }
8476     while ( sideNode != theSideSecondNode );
8477
8478     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8479       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8480       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8481     }
8482   } // end nodes search on the side 2
8483
8484   // ============================
8485   // sew the border to the side 2
8486   // ============================
8487
8488   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8489   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8490
8491   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8492   if ( toMergeConformal && toCreatePolygons )
8493   {
8494     // do not merge quadrangles if polygons are OK (IPAL0052824)
8495     eIt[0] = eSide[0].begin();
8496     eIt[1] = eSide[1].begin();
8497     bool allQuads[2] = { true, true };
8498     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8499       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8500         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8501     }
8502     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8503   }
8504
8505   TListOfListOfNodes nodeGroupsToMerge;
8506   if (( toMergeConformal ) ||
8507       ( theSideIsFreeBorder && !theSideThirdNode )) {
8508
8509     // all nodes are to be merged
8510
8511     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8512          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8513          nIt[0]++, nIt[1]++ )
8514     {
8515       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8516       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8517       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8518     }
8519   }
8520   else {
8521
8522     // insert new nodes into the border and the side to get equal nb of segments
8523
8524     // get normalized parameters of nodes on the borders
8525     vector< double > param[ 2 ];
8526     param[0].resize( maxNbNodes );
8527     param[1].resize( maxNbNodes );
8528     int iNode, iBord;
8529     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8530       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8531       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8532       const SMDS_MeshNode* nPrev = *nIt;
8533       double bordLength = 0;
8534       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8535         const SMDS_MeshNode* nCur = *nIt;
8536         gp_XYZ segment (nCur->X() - nPrev->X(),
8537                         nCur->Y() - nPrev->Y(),
8538                         nCur->Z() - nPrev->Z());
8539         double segmentLen = segment.Modulus();
8540         bordLength += segmentLen;
8541         param[ iBord ][ iNode ] = bordLength;
8542         nPrev = nCur;
8543       }
8544       // normalize within [0,1]
8545       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8546         param[ iBord ][ iNode ] /= bordLength;
8547       }
8548     }
8549
8550     // loop on border segments
8551     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8552     int i[ 2 ] = { 0, 0 };
8553     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8554     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8555
8556     // element can be split while iterating on border if it has two edges in the border
8557     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8558     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8559
8560     TElemOfNodeListMap insertMap;
8561     TElemOfNodeListMap::iterator insertMapIt;
8562     // insertMap is
8563     // key:   elem to insert nodes into
8564     // value: 2 nodes to insert between + nodes to be inserted
8565     do {
8566       bool next[ 2 ] = { false, false };
8567
8568       // find min adjacent segment length after sewing
8569       double nextParam = 10., prevParam = 0;
8570       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8571         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8572           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8573         if ( i[ iBord ] > 0 )
8574           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8575       }
8576       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8577       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8578       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8579
8580       // choose to insert or to merge nodes
8581       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8582       if ( Abs( du ) <= minSegLen * 0.2 ) {
8583         // merge
8584         // ------
8585         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8586         const SMDS_MeshNode* n0 = *nIt[0];
8587         const SMDS_MeshNode* n1 = *nIt[1];
8588         nodeGroupsToMerge.back().push_back( n1 );
8589         nodeGroupsToMerge.back().push_back( n0 );
8590         // position of node of the border changes due to merge
8591         param[ 0 ][ i[0] ] += du;
8592         // move n1 for the sake of elem shape evaluation during insertion.
8593         // n1 will be removed by MergeNodes() anyway
8594         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8595         next[0] = next[1] = true;
8596       }
8597       else {
8598         // insert
8599         // ------
8600         int intoBord = ( du < 0 ) ? 0 : 1;
8601         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8602         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8603         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8604         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8605         if ( intoBord == 1 ) {
8606           // move node of the border to be on a link of elem of the side
8607           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8608           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8609           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8610           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8611         }
8612         elemReplaceMapIt = elemReplaceMap.find( elem );
8613         if ( elemReplaceMapIt != elemReplaceMap.end() )
8614           elem = elemReplaceMapIt->second;
8615
8616         insertMapIt = insertMap.find( elem );
8617         bool  notFound = ( insertMapIt == insertMap.end() );
8618         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8619         if ( otherLink ) {
8620           // insert into another link of the same element:
8621           // 1. perform insertion into the other link of the elem
8622           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8623           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8624           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8625           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8626           // 2. perform insertion into the link of adjacent faces
8627           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8628             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8629           }
8630           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8631             InsertNodesIntoLink( seg, n12, n22, nodeList );
8632           }
8633           if (toCreatePolyedrs) {
8634             // perform insertion into the links of adjacent volumes
8635             UpdateVolumes(n12, n22, nodeList);
8636           }
8637           // 3. find an element appeared on n1 and n2 after the insertion
8638           insertMap.erase( insertMapIt );
8639           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8640           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8641           elem = elem2;
8642         }
8643         if ( notFound || otherLink ) {
8644           // add element and nodes of the side into the insertMap
8645           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8646           (*insertMapIt).second.push_back( n1 );
8647           (*insertMapIt).second.push_back( n2 );
8648         }
8649         // add node to be inserted into elem
8650         (*insertMapIt).second.push_back( nIns );
8651         next[ 1 - intoBord ] = true;
8652       }
8653
8654       // go to the next segment
8655       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8656         if ( next[ iBord ] ) {
8657           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8658             eIt[ iBord ]++;
8659           nPrev[ iBord ] = *nIt[ iBord ];
8660           nIt[ iBord ]++; i[ iBord ]++;
8661         }
8662       }
8663     }
8664     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8665
8666     // perform insertion of nodes into elements
8667
8668     for (insertMapIt = insertMap.begin();
8669          insertMapIt != insertMap.end();
8670          insertMapIt++ )
8671     {
8672       const SMDS_MeshElement* elem = (*insertMapIt).first;
8673       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8674       if ( nodeList.size() < 3 ) continue;
8675       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8676       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8677
8678       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8679
8680       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8681         InsertNodesIntoLink( seg, n1, n2, nodeList );
8682       }
8683
8684       if ( !theSideIsFreeBorder ) {
8685         // look for and insert nodes into the faces adjacent to elem
8686         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8687           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8688         }
8689       }
8690       if (toCreatePolyedrs) {
8691         // perform insertion into the links of adjacent volumes
8692         UpdateVolumes(n1, n2, nodeList);
8693       }
8694     }
8695   } // end: insert new nodes
8696
8697   MergeNodes ( nodeGroupsToMerge );
8698
8699
8700   // Remove coincident segments
8701
8702   // get new segments
8703   TIDSortedElemSet segments;
8704   SMESH_SequenceOfElemPtr newFaces;
8705   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8706   {
8707     if ( !myLastCreatedElems[i] ) continue;
8708     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8709       segments.insert( segments.end(), myLastCreatedElems[i] );
8710     else
8711       newFaces.push_back( myLastCreatedElems[i] );
8712   }
8713   // get segments adjacent to merged nodes
8714   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8715   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8716   {
8717     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8718     if ( nodes.front()->IsNull() ) continue;
8719     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8720     while ( segIt->more() )
8721       segments.insert( segIt->next() );
8722   }
8723
8724   // find coincident
8725   TListOfListOfElementsID equalGroups;
8726   if ( !segments.empty() )
8727     FindEqualElements( segments, equalGroups );
8728   if ( !equalGroups.empty() )
8729   {
8730     // remove from segments those that will be removed
8731     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8732     for ( ; itGroups != equalGroups.end(); ++itGroups )
8733     {
8734       list< smIdType >& group = *itGroups;
8735       list< smIdType >::iterator id = group.begin();
8736       for ( ++id; id != group.end(); ++id )
8737         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8738           segments.erase( seg );
8739     }
8740     // remove equal segments
8741     MergeElements( equalGroups );
8742
8743     // restore myLastCreatedElems
8744     myLastCreatedElems = newFaces;
8745     TIDSortedElemSet::iterator seg = segments.begin();
8746     for ( ; seg != segments.end(); ++seg )
8747       myLastCreatedElems.push_back( *seg );
8748   }
8749
8750   return aResult;
8751 }
8752
8753 //=======================================================================
8754 //function : InsertNodesIntoLink
8755 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8756 //           and theBetweenNode2 and split theElement
8757 //=======================================================================
8758
8759 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8760                                            const SMDS_MeshNode*        theBetweenNode1,
8761                                            const SMDS_MeshNode*        theBetweenNode2,
8762                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8763                                            const bool                  toCreatePoly)
8764 {
8765   if ( !theElement ) return;
8766
8767   SMESHDS_Mesh *aMesh = GetMeshDS();
8768   vector<const SMDS_MeshElement*> newElems;
8769
8770   if ( theElement->GetType() == SMDSAbs_Edge )
8771   {
8772     theNodesToInsert.push_front( theBetweenNode1 );
8773     theNodesToInsert.push_back ( theBetweenNode2 );
8774     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8775     const SMDS_MeshNode* n1 = *n;
8776     for ( ++n; n != theNodesToInsert.end(); ++n )
8777     {
8778       const SMDS_MeshNode* n2 = *n;
8779       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8780         AddToSameGroups( seg, theElement, aMesh );
8781       else
8782         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8783       n1 = n2;
8784     }
8785     theNodesToInsert.pop_front();
8786     theNodesToInsert.pop_back();
8787
8788     if ( theElement->IsQuadratic() ) // add a not split part
8789     {
8790       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8791                                           theElement->end_nodes() );
8792       int iOther = 0, nbN = nodes.size();
8793       for ( ; iOther < nbN; ++iOther )
8794         if ( nodes[iOther] != theBetweenNode1 &&
8795              nodes[iOther] != theBetweenNode2 )
8796           break;
8797       if      ( iOther == 0 )
8798       {
8799         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8800           AddToSameGroups( seg, theElement, aMesh );
8801         else
8802           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8803       }
8804       else if ( iOther == 2 )
8805       {
8806         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8807           AddToSameGroups( seg, theElement, aMesh );
8808         else
8809           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8810       }
8811     }
8812     // treat new elements
8813     for ( size_t i = 0; i < newElems.size(); ++i )
8814       if ( newElems[i] )
8815       {
8816         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8817         myLastCreatedElems.push_back( newElems[i] );
8818       }
8819     ReplaceElemInGroups( theElement, newElems, aMesh );
8820     aMesh->RemoveElement( theElement );
8821     return;
8822
8823   } // if ( theElement->GetType() == SMDSAbs_Edge )
8824
8825   const SMDS_MeshElement* theFace = theElement;
8826   if ( theFace->GetType() != SMDSAbs_Face ) return;
8827
8828   // find indices of 2 link nodes and of the rest nodes
8829   int iNode = 0, il1, il2, i3, i4;
8830   il1 = il2 = i3 = i4 = -1;
8831   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8832
8833   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8834   while ( nodeIt->more() ) {
8835     const SMDS_MeshNode* n = nodeIt->next();
8836     if ( n == theBetweenNode1 )
8837       il1 = iNode;
8838     else if ( n == theBetweenNode2 )
8839       il2 = iNode;
8840     else if ( i3 < 0 )
8841       i3 = iNode;
8842     else
8843       i4 = iNode;
8844     nodes[ iNode++ ] = n;
8845   }
8846   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8847     return ;
8848
8849   // arrange link nodes to go one after another regarding the face orientation
8850   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8851   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8852   if ( reverse ) {
8853     iNode = il1;
8854     il1 = il2;
8855     il2 = iNode;
8856     aNodesToInsert.reverse();
8857   }
8858   // check that not link nodes of a quadrangles are in good order
8859   int nbFaceNodes = theFace->NbNodes();
8860   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8861     iNode = i3;
8862     i3 = i4;
8863     i4 = iNode;
8864   }
8865
8866   if (toCreatePoly || theFace->IsPoly()) {
8867
8868     iNode = 0;
8869     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8870
8871     // add nodes of face up to first node of link
8872     bool isFLN = false;
8873     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8874     while ( nodeIt->more() && !isFLN ) {
8875       const SMDS_MeshNode* n = nodeIt->next();
8876       poly_nodes[iNode++] = n;
8877       isFLN = ( n == nodes[il1] );
8878     }
8879     // add nodes to insert
8880     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8881     for (; nIt != aNodesToInsert.end(); nIt++) {
8882       poly_nodes[iNode++] = *nIt;
8883     }
8884     // add nodes of face starting from last node of link
8885     while ( nodeIt->more() ) {
8886       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8887       poly_nodes[iNode++] = n;
8888     }
8889
8890     // make a new face
8891     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8892   }
8893
8894   else if ( !theFace->IsQuadratic() )
8895   {
8896     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8897     int nbLinkNodes = 2 + aNodesToInsert.size();
8898     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8899     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8900     linkNodes[ 0 ] = nodes[ il1 ];
8901     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8902     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8903     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8904       linkNodes[ iNode++ ] = *nIt;
8905     }
8906     // decide how to split a quadrangle: compare possible variants
8907     // and choose which of splits to be a quadrangle
8908     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8909     if ( nbFaceNodes == 3 ) {
8910       iBestQuad = nbSplits;
8911       i4 = i3;
8912     }
8913     else if ( nbFaceNodes == 4 ) {
8914       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8915       double aBestRate = DBL_MAX;
8916       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8917         i1 = 0; i2 = 1;
8918         double aBadRate = 0;
8919         // evaluate elements quality
8920         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8921           if ( iSplit == iQuad ) {
8922             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8923                                    linkNodes[ i2++ ],
8924                                    nodes[ i3 ],
8925                                    nodes[ i4 ]);
8926             aBadRate += getBadRate( &quad, aCrit );
8927           }
8928           else {
8929             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8930                                    linkNodes[ i2++ ],
8931                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8932             aBadRate += getBadRate( &tria, aCrit );
8933           }
8934         }
8935         // choice
8936         if ( aBadRate < aBestRate ) {
8937           iBestQuad = iQuad;
8938           aBestRate = aBadRate;
8939         }
8940       }
8941     }
8942
8943     // create new elements
8944     i1 = 0; i2 = 1;
8945     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8946     {
8947       if ( iSplit == iBestQuad )
8948         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8949                                             linkNodes[ i2++ ],
8950                                             nodes[ i3 ],
8951                                             nodes[ i4 ]));
8952       else
8953         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8954                                             linkNodes[ i2++ ],
8955                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8956     }
8957
8958     const SMDS_MeshNode* newNodes[ 4 ];
8959     newNodes[ 0 ] = linkNodes[ i1 ];
8960     newNodes[ 1 ] = linkNodes[ i2 ];
8961     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8962     newNodes[ 3 ] = nodes[ i4 ];
8963     if (iSplit == iBestQuad)
8964       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8965     else
8966       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8967
8968   } // end if(!theFace->IsQuadratic())
8969
8970   else { // theFace is quadratic
8971     // we have to split theFace on simple triangles and one simple quadrangle
8972     int tmp = il1/2;
8973     int nbshift = tmp*2;
8974     // shift nodes in nodes[] by nbshift
8975     int i,j;
8976     for(i=0; i<nbshift; i++) {
8977       const SMDS_MeshNode* n = nodes[0];
8978       for(j=0; j<nbFaceNodes-1; j++) {
8979         nodes[j] = nodes[j+1];
8980       }
8981       nodes[nbFaceNodes-1] = n;
8982     }
8983     il1 = il1 - nbshift;
8984     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8985     //   n0      n1     n2    n0      n1     n2
8986     //     +-----+-----+        +-----+-----+
8987     //      \         /         |           |
8988     //       \       /          |           |
8989     //      n5+     +n3       n7+           +n3
8990     //         \   /            |           |
8991     //          \ /             |           |
8992     //           +              +-----+-----+
8993     //           n4           n6      n5     n4
8994
8995     // create new elements
8996     int n1,n2,n3;
8997     if ( nbFaceNodes == 6 ) { // quadratic triangle
8998       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8999       if ( theFace->IsMediumNode(nodes[il1]) ) {
9000         // create quadrangle
9001         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9002         n1 = 1;
9003         n2 = 2;
9004         n3 = 3;
9005       }
9006       else {
9007         // create quadrangle
9008         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9009         n1 = 0;
9010         n2 = 1;
9011         n3 = 5;
9012       }
9013     }
9014     else { // nbFaceNodes==8 - quadratic quadrangle
9015       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9016       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9017       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9018       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9019         // create quadrangle
9020         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9021         n1 = 1;
9022         n2 = 2;
9023         n3 = 3;
9024       }
9025       else {
9026         // create quadrangle
9027         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9028         n1 = 0;
9029         n2 = 1;
9030         n3 = 7;
9031       }
9032     }
9033     // create needed triangles using n1,n2,n3 and inserted nodes
9034     int nbn = 2 + aNodesToInsert.size();
9035     vector<const SMDS_MeshNode*> aNodes(nbn);
9036     aNodes[0    ] = nodes[n1];
9037     aNodes[nbn-1] = nodes[n2];
9038     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9039     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9040       aNodes[iNode++] = *nIt;
9041     }
9042     for ( i = 1; i < nbn; i++ )
9043       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9044   }
9045
9046   // remove the old face
9047   for ( size_t i = 0; i < newElems.size(); ++i )
9048     if ( newElems[i] )
9049     {
9050       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9051       myLastCreatedElems.push_back( newElems[i] );
9052     }
9053   ReplaceElemInGroups( theFace, newElems, aMesh );
9054   aMesh->RemoveElement(theFace);
9055
9056 } // InsertNodesIntoLink()
9057
9058 //=======================================================================
9059 //function : UpdateVolumes
9060 //purpose  :
9061 //=======================================================================
9062
9063 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9064                                       const SMDS_MeshNode*        theBetweenNode2,
9065                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9066 {
9067   ClearLastCreated();
9068
9069   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9070   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9071     const SMDS_MeshElement* elem = invElemIt->next();
9072
9073     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9074     SMDS_VolumeTool aVolume (elem);
9075     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9076       continue;
9077
9078     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9079     int iface, nbFaces = aVolume.NbFaces();
9080     vector<const SMDS_MeshNode *> poly_nodes;
9081     vector<int> quantities (nbFaces);
9082
9083     for (iface = 0; iface < nbFaces; iface++) {
9084       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9085       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9086       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9087
9088       for (int inode = 0; inode < nbFaceNodes; inode++) {
9089         poly_nodes.push_back(faceNodes[inode]);
9090
9091         if (nbInserted == 0) {
9092           if (faceNodes[inode] == theBetweenNode1) {
9093             if (faceNodes[inode + 1] == theBetweenNode2) {
9094               nbInserted = theNodesToInsert.size();
9095
9096               // add nodes to insert
9097               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9098               for (; nIt != theNodesToInsert.end(); nIt++) {
9099                 poly_nodes.push_back(*nIt);
9100               }
9101             }
9102           }
9103           else if (faceNodes[inode] == theBetweenNode2) {
9104             if (faceNodes[inode + 1] == theBetweenNode1) {
9105               nbInserted = theNodesToInsert.size();
9106
9107               // add nodes to insert in reversed order
9108               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9109               nIt--;
9110               for (; nIt != theNodesToInsert.begin(); nIt--) {
9111                 poly_nodes.push_back(*nIt);
9112               }
9113               poly_nodes.push_back(*nIt);
9114             }
9115           }
9116           else {
9117           }
9118         }
9119       }
9120       quantities[iface] = nbFaceNodes + nbInserted;
9121     }
9122
9123     // Replace the volume
9124     SMESHDS_Mesh *aMesh = GetMeshDS();
9125
9126     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9127     {
9128       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9129       myLastCreatedElems.push_back( newElem );
9130       ReplaceElemInGroups( elem, newElem, aMesh );
9131     }
9132     aMesh->RemoveElement( elem );
9133   }
9134 }
9135
9136 namespace
9137 {
9138   //================================================================================
9139   /*!
9140    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9141    */
9142   //================================================================================
9143
9144   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9145                            vector<const SMDS_MeshNode *> & nodes,
9146                            vector<int> &                   nbNodeInFaces )
9147   {
9148     nodes.clear();
9149     nbNodeInFaces.clear();
9150     SMDS_VolumeTool vTool ( elem );
9151     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9152     {
9153       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9154       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9155       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9156     }
9157   }
9158 }
9159
9160 //=======================================================================
9161 /*!
9162  * \brief Convert elements contained in a sub-mesh to quadratic
9163  * \return int - nb of checked elements
9164  */
9165 //=======================================================================
9166
9167 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9168                                                   SMESH_MesherHelper& theHelper,
9169                                                   const bool          theForce3d)
9170 {
9171   //MESSAGE("convertElemToQuadratic");
9172   smIdType nbElem = 0;
9173   if( !theSm ) return nbElem;
9174
9175   vector<int> nbNodeInFaces;
9176   vector<const SMDS_MeshNode *> nodes;
9177   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9178   while(ElemItr->more())
9179   {
9180     nbElem++;
9181     const SMDS_MeshElement* elem = ElemItr->next();
9182     if( !elem ) continue;
9183
9184     // analyse a necessity of conversion
9185     const SMDSAbs_ElementType aType = elem->GetType();
9186     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9187       continue;
9188     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9189     bool hasCentralNodes = false;
9190     if ( elem->IsQuadratic() )
9191     {
9192       bool alreadyOK;
9193       switch ( aGeomType ) {
9194       case SMDSEntity_Quad_Triangle:
9195       case SMDSEntity_Quad_Quadrangle:
9196       case SMDSEntity_Quad_Hexa:
9197       case SMDSEntity_Quad_Penta:
9198         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9199
9200       case SMDSEntity_BiQuad_Triangle:
9201       case SMDSEntity_BiQuad_Quadrangle:
9202       case SMDSEntity_TriQuad_Hexa:
9203       case SMDSEntity_BiQuad_Penta:
9204         alreadyOK = theHelper.GetIsBiQuadratic();
9205         hasCentralNodes = true;
9206         break;
9207       default:
9208         alreadyOK = true;
9209       }
9210       // take into account already present medium nodes
9211       switch ( aType ) {
9212       case SMDSAbs_Volume:
9213         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9214       case SMDSAbs_Face:
9215         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9216       case SMDSAbs_Edge:
9217         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9218       default:;
9219       }
9220       if ( alreadyOK )
9221         continue;
9222     }
9223     // get elem data needed to re-create it
9224     //
9225     const smIdType id = elem->GetID();
9226     const int nbNodes = elem->NbCornerNodes();
9227     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9228     if ( aGeomType == SMDSEntity_Polyhedra )
9229       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9230     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9231       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9232
9233     // remove a linear element
9234     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9235
9236     // remove central nodes of biquadratic elements (biquad->quad conversion)
9237     if ( hasCentralNodes )
9238       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9239         if ( nodes[i]->NbInverseElements() == 0 )
9240           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9241
9242     const SMDS_MeshElement* NewElem = 0;
9243
9244     switch( aType )
9245     {
9246     case SMDSAbs_Edge :
9247     {
9248       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9249       break;
9250     }
9251     case SMDSAbs_Face :
9252     {
9253       switch(nbNodes)
9254       {
9255       case 3:
9256         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9257         break;
9258       case 4:
9259         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9260         break;
9261       default:
9262         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9263       }
9264       break;
9265     }
9266     case SMDSAbs_Volume :
9267     {
9268       switch( aGeomType )
9269       {
9270       case SMDSEntity_Tetra:
9271         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9272         break;
9273       case SMDSEntity_Pyramid:
9274         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9275         break;
9276       case SMDSEntity_Penta:
9277       case SMDSEntity_Quad_Penta:
9278       case SMDSEntity_BiQuad_Penta:
9279         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9280         break;
9281       case SMDSEntity_Hexa:
9282       case SMDSEntity_Quad_Hexa:
9283       case SMDSEntity_TriQuad_Hexa:
9284         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9285                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9286         break;
9287       case SMDSEntity_Hexagonal_Prism:
9288       default:
9289         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9290       }
9291       break;
9292     }
9293     default :
9294       continue;
9295     }
9296     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9297     if( NewElem && NewElem->getshapeId() < 1 )
9298       theSm->AddElement( NewElem );
9299   }
9300   return nbElem;
9301 }
9302 //=======================================================================
9303 //function : ConvertToQuadratic
9304 //purpose  :
9305 //=======================================================================
9306
9307 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9308 {
9309   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9310   SMESHDS_Mesh* meshDS = GetMeshDS();
9311
9312   SMESH_MesherHelper aHelper(*myMesh);
9313
9314   aHelper.SetIsQuadratic( true );
9315   aHelper.SetIsBiQuadratic( theToBiQuad );
9316   aHelper.SetElementsOnShape(true);
9317   aHelper.ToFixNodeParameters( true );
9318
9319   // convert elements assigned to sub-meshes
9320   smIdType nbCheckedElems = 0;
9321   if ( myMesh->HasShapeToMesh() )
9322   {
9323     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9324     {
9325       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9326       while ( smIt->more() ) {
9327         SMESH_subMesh* sm = smIt->next();
9328         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9329           aHelper.SetSubShape( sm->GetSubShape() );
9330           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9331         }
9332       }
9333     }
9334   }
9335
9336   // convert elements NOT assigned to sub-meshes
9337   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9338   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9339   {
9340     aHelper.SetElementsOnShape(false);
9341     SMESHDS_SubMesh *smDS = 0;
9342
9343     // convert edges
9344     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9345     while( aEdgeItr->more() )
9346     {
9347       const SMDS_MeshEdge* edge = aEdgeItr->next();
9348       if ( !edge->IsQuadratic() )
9349       {
9350         smIdType                  id = edge->GetID();
9351         const SMDS_MeshNode* n1 = edge->GetNode(0);
9352         const SMDS_MeshNode* n2 = edge->GetNode(1);
9353
9354         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9355
9356         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9357         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9358       }
9359       else
9360       {
9361         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9362       }
9363     }
9364
9365     // convert faces
9366     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9367     while( aFaceItr->more() )
9368     {
9369       const SMDS_MeshFace* face = aFaceItr->next();
9370       if ( !face ) continue;
9371       
9372       const SMDSAbs_EntityType type = face->GetEntityType();
9373       bool alreadyOK;
9374       switch( type )
9375       {
9376       case SMDSEntity_Quad_Triangle:
9377       case SMDSEntity_Quad_Quadrangle:
9378         alreadyOK = !theToBiQuad;
9379         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9380         break;
9381       case SMDSEntity_BiQuad_Triangle:
9382       case SMDSEntity_BiQuad_Quadrangle:
9383         alreadyOK = theToBiQuad;
9384         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9385         break;
9386       default: alreadyOK = false;
9387       }
9388       if ( alreadyOK )
9389         continue;
9390
9391       const smIdType id = face->GetID();
9392       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9393
9394       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9395
9396       SMDS_MeshFace * NewFace = 0;
9397       switch( type )
9398       {
9399       case SMDSEntity_Triangle:
9400       case SMDSEntity_Quad_Triangle:
9401       case SMDSEntity_BiQuad_Triangle:
9402         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9403         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9404           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9405         break;
9406
9407       case SMDSEntity_Quadrangle:
9408       case SMDSEntity_Quad_Quadrangle:
9409       case SMDSEntity_BiQuad_Quadrangle:
9410         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9411         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9412           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9413         break;
9414
9415       default:;
9416         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9417       }
9418       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9419     }
9420
9421     // convert volumes
9422     vector<int> nbNodeInFaces;
9423     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9424     while(aVolumeItr->more())
9425     {
9426       const SMDS_MeshVolume* volume = aVolumeItr->next();
9427       if ( !volume ) continue;
9428
9429       const SMDSAbs_EntityType type = volume->GetEntityType();
9430       if ( volume->IsQuadratic() )
9431       {
9432         bool alreadyOK;
9433         switch ( type )
9434         {
9435         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9436         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9437         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9438         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9439         default:                      alreadyOK = true;
9440         }
9441         if ( alreadyOK )
9442         {
9443           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9444           continue;
9445         }
9446       }
9447       const smIdType id = volume->GetID();
9448       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9449       if ( type == SMDSEntity_Polyhedra )
9450         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9451       else if ( type == SMDSEntity_Hexagonal_Prism )
9452         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9453
9454       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9455
9456       SMDS_MeshVolume * NewVolume = 0;
9457       switch ( type )
9458       {
9459       case SMDSEntity_Tetra:
9460         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9461         break;
9462       case SMDSEntity_Hexa:
9463       case SMDSEntity_Quad_Hexa:
9464       case SMDSEntity_TriQuad_Hexa:
9465         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9466                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9467         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9468           if ( nodes[i]->NbInverseElements() == 0 )
9469             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9470         break;
9471       case SMDSEntity_Pyramid:
9472         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9473                                       nodes[3], nodes[4], id, theForce3d);
9474         break;
9475       case SMDSEntity_Penta:
9476       case SMDSEntity_Quad_Penta:
9477       case SMDSEntity_BiQuad_Penta:
9478         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9479                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9480         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9481           if ( nodes[i]->NbInverseElements() == 0 )
9482             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9483         break;
9484       case SMDSEntity_Hexagonal_Prism:
9485       default:
9486         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9487       }
9488       ReplaceElemInGroups(volume, NewVolume, meshDS);
9489     }
9490   }
9491
9492   if ( !theForce3d )
9493   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9494     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9495     // aHelper.FixQuadraticElements(myError);
9496     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9497   }
9498 }
9499
9500 //================================================================================
9501 /*!
9502  * \brief Makes given elements quadratic
9503  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9504  *  \param theElements - elements to make quadratic
9505  */
9506 //================================================================================
9507
9508 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9509                                           TIDSortedElemSet& theElements,
9510                                           const bool        theToBiQuad)
9511 {
9512   if ( theElements.empty() ) return;
9513
9514   // we believe that all theElements are of the same type
9515   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9516
9517   // get all nodes shared by theElements
9518   TIDSortedNodeSet allNodes;
9519   TIDSortedElemSet::iterator eIt = theElements.begin();
9520   for ( ; eIt != theElements.end(); ++eIt )
9521     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9522
9523   // complete theElements with elements of lower dim whose all nodes are in allNodes
9524
9525   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9526   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9527   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9528   for ( ; nIt != allNodes.end(); ++nIt )
9529   {
9530     const SMDS_MeshNode* n = *nIt;
9531     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9532     while ( invIt->more() )
9533     {
9534       const SMDS_MeshElement*      e = invIt->next();
9535       const SMDSAbs_ElementType type = e->GetType();
9536       if ( e->IsQuadratic() )
9537       {
9538         quadAdjacentElems[ type ].insert( e );
9539
9540         bool alreadyOK;
9541         switch ( e->GetEntityType() ) {
9542         case SMDSEntity_Quad_Triangle:
9543         case SMDSEntity_Quad_Quadrangle:
9544         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9545         case SMDSEntity_BiQuad_Triangle:
9546         case SMDSEntity_BiQuad_Quadrangle:
9547         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9548         default:                           alreadyOK = true;
9549         }
9550         if ( alreadyOK )
9551           continue;
9552       }
9553       if ( type >= elemType )
9554         continue; // same type or more complex linear element
9555
9556       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9557         continue; // e is already checked
9558
9559       // check nodes
9560       bool allIn = true;
9561       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9562       while ( nodeIt->more() && allIn )
9563         allIn = allNodes.count( nodeIt->next() );
9564       if ( allIn )
9565         theElements.insert(e );
9566     }
9567   }
9568
9569   SMESH_MesherHelper helper(*myMesh);
9570   helper.SetIsQuadratic( true );
9571   helper.SetIsBiQuadratic( theToBiQuad );
9572
9573   // add links of quadratic adjacent elements to the helper
9574
9575   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9576     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9577           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9578     {
9579       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9580     }
9581   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9582     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9583           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9584     {
9585       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9586     }
9587   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9588     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9589           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9590     {
9591       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9592     }
9593
9594   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9595
9596   SMESHDS_Mesh*  meshDS = GetMeshDS();
9597   SMESHDS_SubMesh* smDS = 0;
9598   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9599   {
9600     const SMDS_MeshElement* elem = *eIt;
9601
9602     bool alreadyOK;
9603     int nbCentralNodes = 0;
9604     switch ( elem->GetEntityType() ) {
9605       // linear convertible
9606     case SMDSEntity_Edge:
9607     case SMDSEntity_Triangle:
9608     case SMDSEntity_Quadrangle:
9609     case SMDSEntity_Tetra:
9610     case SMDSEntity_Pyramid:
9611     case SMDSEntity_Hexa:
9612     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9613       // quadratic that can become bi-quadratic
9614     case SMDSEntity_Quad_Triangle:
9615     case SMDSEntity_Quad_Quadrangle:
9616     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9617       // bi-quadratic
9618     case SMDSEntity_BiQuad_Triangle:
9619     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9620     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9621       // the rest
9622     default:                           alreadyOK = true;
9623     }
9624     if ( alreadyOK ) continue;
9625
9626     const SMDSAbs_ElementType type = elem->GetType();
9627     const smIdType              id = elem->GetID();
9628     const int              nbNodes = elem->NbCornerNodes();
9629     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9630
9631     helper.SetSubShape( elem->getshapeId() );
9632
9633     if ( !smDS || !smDS->Contains( elem ))
9634       smDS = meshDS->MeshElements( elem->getshapeId() );
9635     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9636
9637     SMDS_MeshElement * newElem = 0;
9638     switch( nbNodes )
9639     {
9640     case 4: // cases for most frequently used element types go first (for optimization)
9641       if ( type == SMDSAbs_Volume )
9642         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9643       else
9644         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9645       break;
9646     case 8:
9647       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9648                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9649       break;
9650     case 3:
9651       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9652       break;
9653     case 2:
9654       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9655       break;
9656     case 5:
9657       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9658                                  nodes[4], id, theForce3d);
9659       break;
9660     case 6:
9661       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9662                                  nodes[4], nodes[5], id, theForce3d);
9663       break;
9664     default:;
9665     }
9666     ReplaceElemInGroups( elem, newElem, meshDS);
9667     if( newElem && smDS )
9668       smDS->AddElement( newElem );
9669
9670     // remove central nodes
9671     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9672       if ( nodes[i]->NbInverseElements() == 0 )
9673         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9674
9675   } // loop on theElements
9676
9677   if ( !theForce3d )
9678   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9679     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9680     // helper.FixQuadraticElements( myError );
9681     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9682   }
9683 }
9684
9685 //=======================================================================
9686 /*!
9687  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9688  * \return smIdType - nb of checked elements
9689  */
9690 //=======================================================================
9691
9692 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9693                                           SMDS_ElemIteratorPtr theItr,
9694                                           const int            /*theShapeID*/)
9695 {
9696   smIdType nbElem = 0;
9697   SMESHDS_Mesh* meshDS = GetMeshDS();
9698   ElemFeatures elemType;
9699   vector<const SMDS_MeshNode *> nodes;
9700
9701   while( theItr->more() )
9702   {
9703     const SMDS_MeshElement* elem = theItr->next();
9704     nbElem++;
9705     if( elem && elem->IsQuadratic())
9706     {
9707       // get elem data
9708       int nbCornerNodes = elem->NbCornerNodes();
9709       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9710
9711       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9712
9713       //remove a quadratic element
9714       if ( !theSm || !theSm->Contains( elem ))
9715         theSm = meshDS->MeshElements( elem->getshapeId() );
9716       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9717
9718       // remove medium nodes
9719       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9720         if ( nodes[i]->NbInverseElements() == 0 )
9721           meshDS->RemoveFreeNode( nodes[i], theSm );
9722
9723       // add a linear element
9724       nodes.resize( nbCornerNodes );
9725       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9726       ReplaceElemInGroups(elem, newElem, meshDS);
9727       if( theSm && newElem )
9728         theSm->AddElement( newElem );
9729     }
9730   }
9731   return nbElem;
9732 }
9733
9734 //=======================================================================
9735 //function : ConvertFromQuadratic
9736 //purpose  :
9737 //=======================================================================
9738
9739 bool SMESH_MeshEditor::ConvertFromQuadratic()
9740 {
9741   smIdType nbCheckedElems = 0;
9742   if ( myMesh->HasShapeToMesh() )
9743   {
9744     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9745     {
9746       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9747       while ( smIt->more() ) {
9748         SMESH_subMesh* sm = smIt->next();
9749         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9750           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9751       }
9752     }
9753   }
9754
9755   smIdType totalNbElems =
9756     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9757   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9758   {
9759     SMESHDS_SubMesh *aSM = 0;
9760     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9761   }
9762
9763   return true;
9764 }
9765
9766 namespace
9767 {
9768   //================================================================================
9769   /*!
9770    * \brief Return true if all medium nodes of the element are in the node set
9771    */
9772   //================================================================================
9773
9774   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9775   {
9776     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9777       if ( !nodeSet.count( elem->GetNode(i) ))
9778         return false;
9779     return true;
9780   }
9781 }
9782
9783 //================================================================================
9784 /*!
9785  * \brief Makes given elements linear
9786  */
9787 //================================================================================
9788
9789 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9790 {
9791   if ( theElements.empty() ) return;
9792
9793   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9794   set<smIdType> mediumNodeIDs;
9795   TIDSortedElemSet::iterator eIt = theElements.begin();
9796   for ( ; eIt != theElements.end(); ++eIt )
9797   {
9798     const SMDS_MeshElement* e = *eIt;
9799     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9800       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9801   }
9802
9803   // replace given elements by linear ones
9804   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9805   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9806
9807   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9808   // except those elements sharing medium nodes of quadratic element whose medium nodes
9809   // are not all in mediumNodeIDs
9810
9811   // get remaining medium nodes
9812   TIDSortedNodeSet mediumNodes;
9813   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9814   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9815     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9816       mediumNodes.insert( mediumNodes.end(), n );
9817
9818   // find more quadratic elements to convert
9819   TIDSortedElemSet moreElemsToConvert;
9820   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9821   for ( ; nIt != mediumNodes.end(); ++nIt )
9822   {
9823     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9824     while ( invIt->more() )
9825     {
9826       const SMDS_MeshElement* e = invIt->next();
9827       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9828       {
9829         // find a more complex element including e and
9830         // whose medium nodes are not in mediumNodes
9831         bool complexFound = false;
9832         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9833         {
9834           SMDS_ElemIteratorPtr invIt2 =
9835             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9836           while ( invIt2->more() )
9837           {
9838             const SMDS_MeshElement* eComplex = invIt2->next();
9839             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9840             {
9841               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9842               if ( nbCommonNodes == e->NbNodes())
9843               {
9844                 complexFound = true;
9845                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9846                 break;
9847               }
9848             }
9849           }
9850         }
9851         if ( !complexFound )
9852           moreElemsToConvert.insert( e );
9853       }
9854     }
9855   }
9856   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9857   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9858 }
9859
9860 //=======================================================================
9861 //function : SewSideElements
9862 //purpose  :
9863 //=======================================================================
9864
9865 SMESH_MeshEditor::Sew_Error
9866 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9867                                    TIDSortedElemSet&    theSide2,
9868                                    const SMDS_MeshNode* theFirstNode1,
9869                                    const SMDS_MeshNode* theFirstNode2,
9870                                    const SMDS_MeshNode* theSecondNode1,
9871                                    const SMDS_MeshNode* theSecondNode2)
9872 {
9873   ClearLastCreated();
9874
9875   if ( theSide1.size() != theSide2.size() )
9876     return SEW_DIFF_NB_OF_ELEMENTS;
9877
9878   Sew_Error aResult = SEW_OK;
9879   // Algo:
9880   // 1. Build set of faces representing each side
9881   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9882   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9883
9884   // =======================================================================
9885   // 1. Build set of faces representing each side:
9886   // =======================================================================
9887   // a. build set of nodes belonging to faces
9888   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9889   // c. create temporary faces representing side of volumes if correspondent
9890   //    face does not exist
9891
9892   SMESHDS_Mesh* aMesh = GetMeshDS();
9893   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9894   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9895   TIDSortedElemSet             faceSet1, faceSet2;
9896   set<const SMDS_MeshElement*> volSet1,  volSet2;
9897   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9898   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9899   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9900   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9901   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9902   int iSide, iFace, iNode;
9903
9904   list<const SMDS_MeshElement* > tempFaceList;
9905   for ( iSide = 0; iSide < 2; iSide++ ) {
9906     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9907     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9908     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9909     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9910     set<const SMDS_MeshElement*>::iterator vIt;
9911     TIDSortedElemSet::iterator eIt;
9912     set<const SMDS_MeshNode*>::iterator    nIt;
9913
9914     // check that given nodes belong to given elements
9915     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9916     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9917     int firstIndex = -1, secondIndex = -1;
9918     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9919       const SMDS_MeshElement* elem = *eIt;
9920       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9921       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9922       if ( firstIndex > -1 && secondIndex > -1 ) break;
9923     }
9924     if ( firstIndex < 0 || secondIndex < 0 ) {
9925       // we can simply return until temporary faces created
9926       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9927     }
9928
9929     // -----------------------------------------------------------
9930     // 1a. Collect nodes of existing faces
9931     //     and build set of face nodes in order to detect missing
9932     //     faces corresponding to sides of volumes
9933     // -----------------------------------------------------------
9934
9935     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9936
9937     // loop on the given element of a side
9938     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9939       //const SMDS_MeshElement* elem = *eIt;
9940       const SMDS_MeshElement* elem = *eIt;
9941       if ( elem->GetType() == SMDSAbs_Face ) {
9942         faceSet->insert( elem );
9943         set <const SMDS_MeshNode*> faceNodeSet;
9944         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9945         while ( nodeIt->more() ) {
9946           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9947           nodeSet->insert( n );
9948           faceNodeSet.insert( n );
9949         }
9950         setOfFaceNodeSet.insert( faceNodeSet );
9951       }
9952       else if ( elem->GetType() == SMDSAbs_Volume )
9953         volSet->insert( elem );
9954     }
9955     // ------------------------------------------------------------------------------
9956     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9957     // ------------------------------------------------------------------------------
9958
9959     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9960       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9961       while ( fIt->more() ) { // loop on faces sharing a node
9962         const SMDS_MeshElement* f = fIt->next();
9963         if ( faceSet->find( f ) == faceSet->end() ) {
9964           // check if all nodes are in nodeSet and
9965           // complete setOfFaceNodeSet if they are
9966           set <const SMDS_MeshNode*> faceNodeSet;
9967           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9968           bool allInSet = true;
9969           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9970             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9971             if ( nodeSet->find( n ) == nodeSet->end() )
9972               allInSet = false;
9973             else
9974               faceNodeSet.insert( n );
9975           }
9976           if ( allInSet ) {
9977             faceSet->insert( f );
9978             setOfFaceNodeSet.insert( faceNodeSet );
9979           }
9980         }
9981       }
9982     }
9983
9984     // -------------------------------------------------------------------------
9985     // 1c. Create temporary faces representing sides of volumes if correspondent
9986     //     face does not exist
9987     // -------------------------------------------------------------------------
9988
9989     if ( !volSet->empty() ) {
9990       //int nodeSetSize = nodeSet->size();
9991
9992       // loop on given volumes
9993       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9994         SMDS_VolumeTool vol (*vIt);
9995         // loop on volume faces: find free faces
9996         // --------------------------------------
9997         list<const SMDS_MeshElement* > freeFaceList;
9998         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9999           if ( !vol.IsFreeFace( iFace ))
10000             continue;
10001           // check if there is already a face with same nodes in a face set
10002           const SMDS_MeshElement* aFreeFace = 0;
10003           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10004           int nbNodes = vol.NbFaceNodes( iFace );
10005           set <const SMDS_MeshNode*> faceNodeSet;
10006           vol.GetFaceNodes( iFace, faceNodeSet );
10007           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10008           if ( isNewFace ) {
10009             // no such a face is given but it still can exist, check it
10010             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10011             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10012           }
10013           if ( !aFreeFace ) {
10014             // create a temporary face
10015             if ( nbNodes == 3 ) {
10016               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10017               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10018             }
10019             else if ( nbNodes == 4 ) {
10020               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10021               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10022             }
10023             else {
10024               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10025               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10026               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10027             }
10028             if ( aFreeFace )
10029               tempFaceList.push_back( aFreeFace );
10030           }
10031
10032           if ( aFreeFace )
10033             freeFaceList.push_back( aFreeFace );
10034
10035         } // loop on faces of a volume
10036
10037         // choose one of several free faces of a volume
10038         // --------------------------------------------
10039         if ( freeFaceList.size() > 1 ) {
10040           // choose a face having max nb of nodes shared by other elems of a side
10041           int maxNbNodes = -1;
10042           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10043           while ( fIt != freeFaceList.end() ) { // loop on free faces
10044             int nbSharedNodes = 0;
10045             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10046             while ( nodeIt->more() ) { // loop on free face nodes
10047               const SMDS_MeshNode* n =
10048                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10049               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10050               while ( invElemIt->more() ) {
10051                 const SMDS_MeshElement* e = invElemIt->next();
10052                 nbSharedNodes += faceSet->count( e );
10053                 nbSharedNodes += elemSet->count( e );
10054               }
10055             }
10056             if ( nbSharedNodes > maxNbNodes ) {
10057               maxNbNodes = nbSharedNodes;
10058               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10059             }
10060             else if ( nbSharedNodes == maxNbNodes ) {
10061               fIt++;
10062             }
10063             else {
10064               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10065             }
10066           }
10067           if ( freeFaceList.size() > 1 )
10068           {
10069             // could not choose one face, use another way
10070             // choose a face most close to the bary center of the opposite side
10071             gp_XYZ aBC( 0., 0., 0. );
10072             set <const SMDS_MeshNode*> addedNodes;
10073             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10074             eIt = elemSet2->begin();
10075             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10076               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10077               while ( nodeIt->more() ) { // loop on free face nodes
10078                 const SMDS_MeshNode* n =
10079                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10080                 if ( addedNodes.insert( n ).second )
10081                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10082               }
10083             }
10084             aBC /= addedNodes.size();
10085             double minDist = DBL_MAX;
10086             fIt = freeFaceList.begin();
10087             while ( fIt != freeFaceList.end() ) { // loop on free faces
10088               double dist = 0;
10089               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10090               while ( nodeIt->more() ) { // loop on free face nodes
10091                 const SMDS_MeshNode* n =
10092                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10093                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10094                 dist += ( aBC - p ).SquareModulus();
10095               }
10096               if ( dist < minDist ) {
10097                 minDist = dist;
10098                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10099               }
10100               else
10101                 fIt = freeFaceList.erase( fIt++ );
10102             }
10103           }
10104         } // choose one of several free faces of a volume
10105
10106         if ( freeFaceList.size() == 1 ) {
10107           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10108           faceSet->insert( aFreeFace );
10109           // complete a node set with nodes of a found free face
10110           //           for ( iNode = 0; iNode < ; iNode++ )
10111           //             nodeSet->insert( fNodes[ iNode ] );
10112         }
10113
10114       } // loop on volumes of a side
10115
10116       //       // complete a set of faces if new nodes in a nodeSet appeared
10117       //       // ----------------------------------------------------------
10118       //       if ( nodeSetSize != nodeSet->size() ) {
10119       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10120       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10121       //           while ( fIt->more() ) { // loop on faces sharing a node
10122       //             const SMDS_MeshElement* f = fIt->next();
10123       //             if ( faceSet->find( f ) == faceSet->end() ) {
10124       //               // check if all nodes are in nodeSet and
10125       //               // complete setOfFaceNodeSet if they are
10126       //               set <const SMDS_MeshNode*> faceNodeSet;
10127       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10128       //               bool allInSet = true;
10129       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10130       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10132       //                   allInSet = false;
10133       //                 else
10134       //                   faceNodeSet.insert( n );
10135       //               }
10136       //               if ( allInSet ) {
10137       //                 faceSet->insert( f );
10138       //                 setOfFaceNodeSet.insert( faceNodeSet );
10139       //               }
10140       //             }
10141       //           }
10142       //         }
10143       //       }
10144     } // Create temporary faces, if there are volumes given
10145   } // loop on sides
10146
10147   if ( faceSet1.size() != faceSet2.size() ) {
10148     // delete temporary faces: they are in reverseElements of actual nodes
10149     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10150     //    while ( tmpFaceIt->more() )
10151     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10152     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10153     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10154     //      aMesh->RemoveElement(*tmpFaceIt);
10155     MESSAGE("Diff nb of faces");
10156     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10157   }
10158
10159   // ============================================================
10160   // 2. Find nodes to merge:
10161   //              bind a node to remove to a node to put instead
10162   // ============================================================
10163
10164   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10165   if ( theFirstNode1 != theFirstNode2 )
10166     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10167   if ( theSecondNode1 != theSecondNode2 )
10168     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10169
10170   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10171   set< long > linkIdSet; // links to process
10172   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10173
10174   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10175   list< NLink > linkList[2];
10176   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10177   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10178   // loop on links in linkList; find faces by links and append links
10179   // of the found faces to linkList
10180   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10181   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10182   {
10183     NLink link[] = { *linkIt[0], *linkIt[1] };
10184     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10185     if ( !linkIdSet.count( linkID ) )
10186       continue;
10187
10188     // by links, find faces in the face sets,
10189     // and find indices of link nodes in the found faces;
10190     // in a face set, there is only one or no face sharing a link
10191     // ---------------------------------------------------------------
10192
10193     const SMDS_MeshElement* face[] = { 0, 0 };
10194     vector<const SMDS_MeshNode*> fnodes[2];
10195     int iLinkNode[2][2];
10196     TIDSortedElemSet avoidSet;
10197     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10198       const SMDS_MeshNode* n1 = link[iSide].first;
10199       const SMDS_MeshNode* n2 = link[iSide].second;
10200       //cout << "Side " << iSide << " ";
10201       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10202       // find a face by two link nodes
10203       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10204                                                       *faceSetPtr[ iSide ], avoidSet,
10205                                                       &iLinkNode[iSide][0],
10206                                                       &iLinkNode[iSide][1] );
10207       if ( face[ iSide ])
10208       {
10209         //cout << " F " << face[ iSide]->GetID() <<endl;
10210         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10211         // put face nodes to fnodes
10212         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10213         fnodes[ iSide ].assign( nIt, nEnd );
10214         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10215       }
10216     }
10217
10218     // check similarity of elements of the sides
10219     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10220       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10221       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10222         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10223       }
10224       else {
10225         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10226       }
10227       break; // do not return because it's necessary to remove tmp faces
10228     }
10229
10230     // set nodes to merge
10231     // -------------------
10232
10233     if ( face[0] && face[1] )  {
10234       const int nbNodes = face[0]->NbNodes();
10235       if ( nbNodes != face[1]->NbNodes() ) {
10236         MESSAGE("Diff nb of face nodes");
10237         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10238         break; // do not return because it s necessary to remove tmp faces
10239       }
10240       bool reverse[] = { false, false }; // order of nodes in the link
10241       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10242         // analyse link orientation in faces
10243         int i1 = iLinkNode[ iSide ][ 0 ];
10244         int i2 = iLinkNode[ iSide ][ 1 ];
10245         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10246       }
10247       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10248       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10249       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10250       {
10251         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10252                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10253       }
10254
10255       // add other links of the faces to linkList
10256       // -----------------------------------------
10257
10258       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10259         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10260         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10261         if ( !iter_isnew.second ) { // already in a set: no need to process
10262           linkIdSet.erase( iter_isnew.first );
10263         }
10264         else // new in set == encountered for the first time: add
10265         {
10266           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10267           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10268           linkList[0].push_back ( NLink( n1, n2 ));
10269           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10270         }
10271       }
10272     } // 2 faces found
10273
10274     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10275       break;
10276
10277   } // loop on link lists
10278
10279   if ( aResult == SEW_OK &&
10280        ( //linkIt[0] != linkList[0].end() ||
10281         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10282     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10283              " " << (faceSetPtr[1]->empty()));
10284     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10285   }
10286
10287   // ====================================================================
10288   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10289   // ====================================================================
10290
10291   // delete temporary faces
10292   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10293   //  while ( tmpFaceIt->more() )
10294   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10295   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10296   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10297     aMesh->RemoveElement(*tmpFaceIt);
10298
10299   if ( aResult != SEW_OK)
10300     return aResult;
10301
10302   list< smIdType > nodeIDsToRemove;
10303   vector< const SMDS_MeshNode*> nodes;
10304   ElemFeatures elemType;
10305
10306   // loop on nodes replacement map
10307   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10308   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10309     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10310     {
10311       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10312       nodeIDsToRemove.push_back( nToRemove->GetID() );
10313       // loop on elements sharing nToRemove
10314       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10315       while ( invElemIt->more() ) {
10316         const SMDS_MeshElement* e = invElemIt->next();
10317         // get a new suite of nodes: make replacement
10318         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10319         nodes.resize( nbNodes );
10320         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10321         while ( nIt->more() ) {
10322           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10323           nnIt = nReplaceMap.find( n );
10324           if ( nnIt != nReplaceMap.end() ) {
10325             nbReplaced++;
10326             n = (*nnIt).second;
10327           }
10328           nodes[ i++ ] = n;
10329         }
10330         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10331         //         elemIDsToRemove.push_back( e->GetID() );
10332         //       else
10333         if ( nbReplaced )
10334         {
10335           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10336           aMesh->RemoveElement( e );
10337
10338           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10339           {
10340             AddToSameGroups( newElem, e, aMesh );
10341             if ( int aShapeId = e->getshapeId() )
10342               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10343           }
10344         }
10345       }
10346     }
10347
10348   Remove( nodeIDsToRemove, true );
10349
10350   return aResult;
10351 }
10352
10353 //================================================================================
10354 /*!
10355  * \brief Find corresponding nodes in two sets of faces
10356  * \param theSide1 - first face set
10357  * \param theSide2 - second first face
10358  * \param theFirstNode1 - a boundary node of set 1
10359  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10360  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10361  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10362  * \param nReplaceMap - output map of corresponding nodes
10363  * \return bool  - is a success or not
10364  */
10365 //================================================================================
10366
10367 #ifdef _DEBUG_
10368 //#define DEBUG_MATCHING_NODES
10369 #endif
10370
10371 SMESH_MeshEditor::Sew_Error
10372 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10373                                     set<const SMDS_MeshElement*>& theSide2,
10374                                     const SMDS_MeshNode*          theFirstNode1,
10375                                     const SMDS_MeshNode*          theFirstNode2,
10376                                     const SMDS_MeshNode*          theSecondNode1,
10377                                     const SMDS_MeshNode*          theSecondNode2,
10378                                     TNodeNodeMap &                nReplaceMap)
10379 {
10380   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10381
10382   nReplaceMap.clear();
10383   //if ( theFirstNode1 != theFirstNode2 )
10384   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10385   //if ( theSecondNode1 != theSecondNode2 )
10386   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10387
10388   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10389   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10390
10391   list< NLink > linkList[2];
10392   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10393   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10394
10395   // loop on links in linkList; find faces by links and append links
10396   // of the found faces to linkList
10397   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10398   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10399     NLink link[] = { *linkIt[0], *linkIt[1] };
10400     if ( linkSet.find( link[0] ) == linkSet.end() )
10401       continue;
10402
10403     // by links, find faces in the face sets,
10404     // and find indices of link nodes in the found faces;
10405     // in a face set, there is only one or no face sharing a link
10406     // ---------------------------------------------------------------
10407
10408     const SMDS_MeshElement* face[] = { 0, 0 };
10409     list<const SMDS_MeshNode*> notLinkNodes[2];
10410     //bool reverse[] = { false, false }; // order of notLinkNodes
10411     int nbNodes[2];
10412     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10413     {
10414       const SMDS_MeshNode* n1 = link[iSide].first;
10415       const SMDS_MeshNode* n2 = link[iSide].second;
10416       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10417       set< const SMDS_MeshElement* > facesOfNode1;
10418       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10419       {
10420         // during a loop of the first node, we find all faces around n1,
10421         // during a loop of the second node, we find one face sharing both n1 and n2
10422         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10423         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10424         while ( fIt->more() ) { // loop on faces sharing a node
10425           const SMDS_MeshElement* f = fIt->next();
10426           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10427               ! facesOfNode1.insert( f ).second ) // f encounters twice
10428           {
10429             if ( face[ iSide ] ) {
10430               MESSAGE( "2 faces per link " );
10431               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10432             }
10433             face[ iSide ] = f;
10434             faceSet->erase( f );
10435
10436             // get not link nodes
10437             int nbN = f->NbNodes();
10438             if ( f->IsQuadratic() )
10439               nbN /= 2;
10440             nbNodes[ iSide ] = nbN;
10441             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10442             int i1 = f->GetNodeIndex( n1 );
10443             int i2 = f->GetNodeIndex( n2 );
10444             int iEnd = nbN, iBeg = -1, iDelta = 1;
10445             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10446             if ( reverse ) {
10447               std::swap( iEnd, iBeg ); iDelta = -1;
10448             }
10449             int i = i2;
10450             while ( true ) {
10451               i += iDelta;
10452               if ( i == iEnd ) i = iBeg + iDelta;
10453               if ( i == i1 ) break;
10454               nodes.push_back ( f->GetNode( i ) );
10455             }
10456           }
10457         }
10458       }
10459     }
10460     // check similarity of elements of the sides
10461     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10462       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10463       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10464         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10465       }
10466       else {
10467         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10468       }
10469     }
10470
10471     // set nodes to merge
10472     // -------------------
10473
10474     if ( face[0] && face[1] )  {
10475       if ( nbNodes[0] != nbNodes[1] ) {
10476         MESSAGE("Diff nb of face nodes");
10477         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10478       }
10479 #ifdef DEBUG_MATCHING_NODES
10480       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10481                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10482                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10483 #endif
10484       int nbN = nbNodes[0];
10485       {
10486         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10487         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10488         for ( int i = 0 ; i < nbN - 2; ++i ) {
10489 #ifdef DEBUG_MATCHING_NODES
10490           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10491 #endif
10492           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10493         }
10494       }
10495
10496       // add other links of the face 1 to linkList
10497       // -----------------------------------------
10498
10499       const SMDS_MeshElement* f0 = face[0];
10500       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10501       for ( int i = 0; i < nbN; i++ )
10502       {
10503         const SMDS_MeshNode* n2 = f0->GetNode( i );
10504         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10505           linkSet.insert( SMESH_TLink( n1, n2 ));
10506         if ( !iter_isnew.second ) { // already in a set: no need to process
10507           linkSet.erase( iter_isnew.first );
10508         }
10509         else // new in set == encountered for the first time: add
10510         {
10511 #ifdef DEBUG_MATCHING_NODES
10512           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10513                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10514 #endif
10515           linkList[0].push_back ( NLink( n1, n2 ));
10516           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10517         }
10518         n1 = n2;
10519       }
10520     } // 2 faces found
10521   } // loop on link lists
10522
10523   return SEW_OK;
10524 }
10525
10526 namespace // automatically find theAffectedElems for DoubleNodes()
10527 {
10528   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10529
10530   //--------------------------------------------------------------------------------
10531   // Nodes shared by adjacent FissureBorder's.
10532   // 1 node  if FissureBorder separates faces
10533   // 2 nodes if FissureBorder separates volumes
10534   struct SubBorder
10535   {
10536     const SMDS_MeshNode* _nodes[2];
10537     int                  _nbNodes;
10538
10539     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10540     {
10541       _nodes[0] = n1;
10542       _nodes[1] = n2;
10543       _nbNodes = bool( n1 ) + bool( n2 );
10544       if ( _nbNodes == 2 && n1 > n2 )
10545         std::swap( _nodes[0], _nodes[1] );
10546     }
10547     bool operator<( const SubBorder& other ) const
10548     {
10549       for ( int i = 0; i < _nbNodes; ++i )
10550       {
10551         if ( _nodes[i] < other._nodes[i] ) return true;
10552         if ( _nodes[i] > other._nodes[i] ) return false;
10553       }
10554       return false;
10555     }
10556   };
10557
10558   //--------------------------------------------------------------------------------
10559   // Map a SubBorder to all FissureBorder it bounds
10560   struct FissureBorder;
10561   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10562   typedef TBorderLinks::iterator                               TMappedSub;
10563
10564   //--------------------------------------------------------------------------------
10565   /*!
10566    * \brief Element border (volume facet or face edge) at a fissure
10567    */
10568   struct FissureBorder
10569   {
10570     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10571     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10572
10573     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10574     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10575
10576     FissureBorder( FissureBorder && from ) // move constructor
10577     {
10578       std::swap( _nodes,       from._nodes );
10579       std::swap( _sortedNodes, from._sortedNodes );
10580       _elems[0] = from._elems[0];
10581       _elems[1] = from._elems[1];
10582     }
10583
10584     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10585                    std::vector< const SMDS_MeshElement* > & adjElems)
10586       : _nodes( elemToDuplicate->NbCornerNodes() )
10587     {
10588       for ( size_t i = 0; i < _nodes.size(); ++i )
10589         _nodes[i] = elemToDuplicate->GetNode( i );
10590
10591       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10592       findAdjacent( type, adjElems );
10593     }
10594
10595     FissureBorder( const SMDS_MeshNode**                    nodes,
10596                    const size_t                             nbNodes,
10597                    const SMDSAbs_ElementType                adjElemsType,
10598                    std::vector< const SMDS_MeshElement* > & adjElems)
10599       : _nodes( nodes, nodes + nbNodes )
10600     {
10601       findAdjacent( adjElemsType, adjElems );
10602     }
10603
10604     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10605                        std::vector< const SMDS_MeshElement* > & adjElems)
10606     {
10607       _elems[0] = _elems[1] = 0;
10608       adjElems.clear();
10609       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10610         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10611           _elems[i] = adjElems[i];
10612     }
10613
10614     bool operator<( const FissureBorder& other ) const
10615     {
10616       return GetSortedNodes() < other.GetSortedNodes();
10617     }
10618
10619     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10620     {
10621       if ( _sortedNodes.empty() && !_nodes.empty() )
10622       {
10623         FissureBorder* me = const_cast<FissureBorder*>( this );
10624         me->_sortedNodes = me->_nodes;
10625         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10626       }
10627       return _sortedNodes;
10628     }
10629
10630     size_t NbSub() const
10631     {
10632       return _nodes.size();
10633     }
10634
10635     SubBorder Sub(size_t i) const
10636     {
10637       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10638     }
10639
10640     void AddSelfTo( TBorderLinks& borderLinks )
10641     {
10642       _mappedSubs.resize( NbSub() );
10643       for ( size_t i = 0; i < NbSub(); ++i )
10644       {
10645         TBorderLinks::iterator s2b =
10646           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10647         s2b->second.push_back( this );
10648         _mappedSubs[ i ] = s2b;
10649       }
10650     }
10651
10652     void Clear()
10653     {
10654       _nodes.clear();
10655     }
10656
10657     const SMDS_MeshElement* GetMarkedElem() const
10658     {
10659       if ( _nodes.empty() ) return 0; // cleared
10660       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10661       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10662       return 0;
10663     }
10664
10665     gp_XYZ GetNorm() const // normal to the border
10666     {
10667       gp_XYZ norm;
10668       if ( _nodes.size() == 2 )
10669       {
10670         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10671         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10672           avgNorm += norm;
10673         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10674           avgNorm += norm;
10675
10676         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10677         norm = bordDir ^ avgNorm;
10678       }
10679       else
10680       {
10681         SMESH_NodeXYZ p0( _nodes[0] );
10682         SMESH_NodeXYZ p1( _nodes[1] );
10683         SMESH_NodeXYZ p2( _nodes[2] );
10684         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10685       }
10686       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10687         norm.Reverse();
10688
10689       return norm;
10690     }
10691
10692     void ChooseSide() // mark an _elem located at positive side of fissure
10693     {
10694       _elems[0]->setIsMarked( true );
10695       gp_XYZ norm = GetNorm();
10696       double maxX = norm.Coord(1);
10697       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10698       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10699       if ( maxX < 0 )
10700       {
10701         _elems[0]->setIsMarked( false );
10702         if ( _elems[1] )
10703           _elems[1]->setIsMarked( true );
10704       }
10705     }
10706
10707   }; // struct FissureBorder
10708
10709   //--------------------------------------------------------------------------------
10710   /*!
10711    * \brief Classifier of elements at fissure edge
10712    */
10713   class FissureNormal
10714   {
10715     std::vector< gp_XYZ > _normals;
10716     bool                  _bothIn;
10717
10718   public:
10719     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10720     {
10721       _bothIn = false;
10722       _normals.reserve(2);
10723       _normals.push_back( bord.GetNorm() );
10724       if ( _normals.size() == 2 )
10725         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10726     }
10727
10728     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10729     {
10730       bool isIn = false;
10731       switch ( _normals.size() ) {
10732       case 1:
10733       {
10734         isIn = !isOut( n, _normals[0], elem );
10735         break;
10736       }
10737       case 2:
10738       {
10739         bool in1 = !isOut( n, _normals[0], elem );
10740         bool in2 = !isOut( n, _normals[1], elem );
10741         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10742       }
10743       }
10744       return isIn;
10745     }
10746   };
10747
10748   //================================================================================
10749   /*!
10750    * \brief Classify an element by a plane passing through a node
10751    */
10752   //================================================================================
10753
10754   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10755   {
10756     SMESH_NodeXYZ p = n;
10757     double sumDot = 0;
10758     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10759     {
10760       SMESH_NodeXYZ pi = elem->GetNode( i );
10761       sumDot += norm * ( pi - p );
10762     }
10763     return sumDot < -1e-100;
10764   }
10765
10766   //================================================================================
10767   /*!
10768    * \brief Find FissureBorder's by nodes to duplicate
10769    */
10770   //================================================================================
10771
10772   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10773                            std::vector< FissureBorder > & theFissureBorders )
10774   {
10775     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10776     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10777     if ( !n ) return;
10778     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10779     if ( n->NbInverseElements( elemType ) == 0 )
10780     {
10781       elemType = SMDSAbs_Face;
10782       if ( n->NbInverseElements( elemType ) == 0 )
10783         return;
10784     }
10785     // unmark elements touching the fissure
10786     for ( ; nIt != theNodes.end(); ++nIt )
10787       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10788
10789     // loop on elements touching the fissure to get their borders belonging to the fissure
10790     std::set< FissureBorder >              fissureBorders;
10791     std::vector< const SMDS_MeshElement* > adjElems;
10792     std::vector< const SMDS_MeshNode* >    nodes;
10793     SMDS_VolumeTool volTool;
10794     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10795     {
10796       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10797       while ( invIt->more() )
10798       {
10799         const SMDS_MeshElement* eInv = invIt->next();
10800         if ( eInv->isMarked() ) continue;
10801         eInv->setIsMarked( true );
10802
10803         if ( elemType == SMDSAbs_Volume )
10804         {
10805           volTool.Set( eInv );
10806           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10807           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10808           {
10809             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10810             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10811             nodes.clear();
10812             bool allOnFissure = true;
10813             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10814               if (( allOnFissure = theNodes.count( nn[ iN ])))
10815                 nodes.push_back( nn[ iN ]);
10816             if ( allOnFissure )
10817               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10818                                                                elemType, adjElems )));
10819           }
10820         }
10821         else // elemType == SMDSAbs_Face
10822         {
10823           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10824           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10825           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10826           {
10827             nn[1]      = eInv->GetNode( iN );
10828             onFissure1 = theNodes.count( nn[1] );
10829             if ( onFissure0 && onFissure1 )
10830               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10831             nn[0]      = nn[1];
10832             onFissure0 = onFissure1;
10833           }
10834         }
10835       }
10836     }
10837
10838     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10839     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10840     for ( ; bord != fissureBorders.end(); ++bord )
10841     {
10842       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10843     }
10844     return;
10845   } // findFissureBorders()
10846
10847   //================================================================================
10848   /*!
10849    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10850    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10851    *  \param [in] theNodesNot - nodes not to duplicate
10852    *  \param [out] theAffectedElems - the found elements
10853    */
10854   //================================================================================
10855
10856   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10857                           TIDSortedElemSet&       theAffectedElems)
10858   {
10859     if ( theElemsOrNodes.empty() ) return;
10860
10861     // find FissureBorder's
10862
10863     std::vector< FissureBorder >           fissure;
10864     std::vector< const SMDS_MeshElement* > elemsByFacet;
10865
10866     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10867     if ( (*elIt)->GetType() == SMDSAbs_Node )
10868     {
10869       findFissureBorders( theElemsOrNodes, fissure );
10870     }
10871     else
10872     {
10873       fissure.reserve( theElemsOrNodes.size() );
10874       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10875       {
10876         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10877         if ( !fissure.back()._elems[1] )
10878           fissure.pop_back();
10879       }
10880     }
10881     if ( fissure.empty() )
10882       return;
10883
10884     // fill borderLinks
10885
10886     TBorderLinks borderLinks;
10887
10888     for ( size_t i = 0; i < fissure.size(); ++i )
10889     {
10890       fissure[i].AddSelfTo( borderLinks );
10891     }
10892
10893     // get theAffectedElems
10894
10895     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10896     for ( size_t i = 0; i < fissure.size(); ++i )
10897       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10898       {
10899         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10900                                         false, /*markElem=*/true );
10901       }
10902
10903     std::vector<const SMDS_MeshNode *>                 facetNodes;
10904     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10905     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10906
10907     // choose a side of fissure
10908     fissure[0].ChooseSide();
10909     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10910
10911     size_t nbCheckedBorders = 0;
10912     while ( nbCheckedBorders < fissure.size() )
10913     {
10914       // find a FissureBorder to treat
10915       FissureBorder* bord = 0;
10916       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10917         if ( fissure[i].GetMarkedElem() )
10918           bord = & fissure[i];
10919       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10920         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10921         {
10922           bord = & fissure[i];
10923           bord->ChooseSide();
10924           theAffectedElems.insert( bord->GetMarkedElem() );
10925         }
10926       if ( !bord ) return;
10927       ++nbCheckedBorders;
10928
10929       // treat FissureBorder's linked to bord
10930       fissureNodes.clear();
10931       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10932       for ( size_t i = 0; i < bord->NbSub(); ++i )
10933       {
10934         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10935         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10936         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10937         const SubBorder&                          sb = l2b->first;
10938         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10939
10940         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10941         {
10942           for ( int j = 0; j < sb._nbNodes; ++j )
10943             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10944           continue;
10945         }
10946
10947         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10948         // until an elem adjacent to a neighbour FissureBorder is found
10949         facetNodes.clear();
10950         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10951         facetNodes.resize( sb._nbNodes + 1 );
10952
10953         while ( bordElem )
10954         {
10955           // check if bordElem is adjacent to a neighbour FissureBorder
10956           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10957           {
10958             FissureBorder* bord2 = linkedBorders[j];
10959             if ( bord2 == bord ) continue;
10960             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10961               bordElem = 0;
10962             else
10963               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10964           }
10965           if ( !bordElem )
10966             break;
10967
10968           // find the next bordElem
10969           const SMDS_MeshElement* nextBordElem = 0;
10970           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10971           {
10972             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10973             if ( fissureNodes.count( n )) continue;
10974
10975             facetNodes[ sb._nbNodes ] = n;
10976             elemsByFacet.clear();
10977             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10978             {
10979               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10980                 if ( elemsByFacet[ iE ] != bordElem &&
10981                      !elemsByFacet[ iE ]->isMarked() )
10982                 {
10983                   theAffectedElems.insert( elemsByFacet[ iE ]);
10984                   elemsByFacet[ iE ]->setIsMarked( true );
10985                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10986                     nextBordElem = elemsByFacet[ iE ];
10987                 }
10988             }
10989           }
10990           bordElem = nextBordElem;
10991
10992         } // while ( bordElem )
10993
10994         linkedBorders.clear(); // not to treat this link any more
10995
10996       } // loop on SubBorder's of a FissureBorder
10997
10998       bord->Clear();
10999
11000     } // loop on FissureBorder's
11001
11002
11003     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11004
11005     // mark nodes of theAffectedElems
11006     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11007
11008     // unmark nodes of the fissure
11009     elIt = theElemsOrNodes.begin();
11010     if ( (*elIt)->GetType() == SMDSAbs_Node )
11011       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11012     else
11013       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11014
11015     std::vector< gp_XYZ > normVec;
11016
11017     // loop on nodes of the fissure, add elements having marked nodes
11018     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11019     {
11020       const SMDS_MeshElement* e = (*elIt);
11021       if ( e->GetType() != SMDSAbs_Node )
11022         e->setIsMarked( true ); // avoid adding a fissure element
11023
11024       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11025       {
11026         const SMDS_MeshNode* n = e->GetNode( iN );
11027         if ( fissEdgeNodes2Norm.count( n ))
11028           continue;
11029
11030         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11031         while ( invIt->more() )
11032         {
11033           const SMDS_MeshElement* eInv = invIt->next();
11034           if ( eInv->isMarked() ) continue;
11035           eInv->setIsMarked( true );
11036
11037           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11038           while( nIt->more() )
11039             if ( nIt->next()->isMarked())
11040             {
11041               theAffectedElems.insert( eInv );
11042               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11043               n->setIsMarked( false );
11044               break;
11045             }
11046         }
11047       }
11048     }
11049
11050     // add elements on the fissure edge
11051     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11052     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11053     {
11054       const SMDS_MeshNode* edgeNode = n2N->first;
11055       const FissureNormal & normals = n2N->second;
11056
11057       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11058       while ( invIt->more() )
11059       {
11060         const SMDS_MeshElement* eInv = invIt->next();
11061         if ( eInv->isMarked() ) continue;
11062         eInv->setIsMarked( true );
11063
11064         // classify eInv using normals
11065         bool toAdd = normals.IsIn( edgeNode, eInv );
11066         if ( toAdd ) // check if all nodes lie on the fissure edge
11067         {
11068           bool notOnEdge = false;
11069           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
11070             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11071           toAdd = notOnEdge;
11072         }
11073         if ( toAdd )
11074         {
11075           theAffectedElems.insert( eInv );
11076         }
11077       }
11078     }
11079
11080     return;
11081   } // findAffectedElems()
11082 } // namespace
11083
11084 //================================================================================
11085 /*!
11086  * \brief Create elements equal (on same nodes) to given ones
11087  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
11088  *              elements of the uppest dimension are duplicated.
11089  */
11090 //================================================================================
11091
11092 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11093 {
11094   ClearLastCreated();
11095   SMESHDS_Mesh* mesh = GetMeshDS();
11096
11097   // get an element type and an iterator over elements
11098
11099   SMDSAbs_ElementType type = SMDSAbs_All;
11100   SMDS_ElemIteratorPtr elemIt;
11101   if ( theElements.empty() )
11102   {
11103     if ( mesh->NbNodes() == 0 )
11104       return;
11105     // get most complex type
11106     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11107       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11108       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11109     };
11110     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11111       if ( mesh->GetMeshInfo().NbElements( types[i] ))
11112       {
11113         type = types[i];
11114         elemIt = mesh->elementsIterator( type );
11115         break;
11116       }
11117   }
11118   else
11119   {
11120     //type = (*theElements.begin())->GetType();
11121     elemIt = SMESHUtils::elemSetIterator( theElements );
11122   }
11123
11124   // un-mark all elements to avoid duplicating just created elements
11125   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11126
11127   // duplicate elements
11128
11129   ElemFeatures elemType;
11130
11131   vector< const SMDS_MeshNode* > nodes;
11132   while ( elemIt->more() )
11133   {
11134     const SMDS_MeshElement* elem = elemIt->next();
11135     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11136         ( elem->isMarked() ))
11137       continue;
11138
11139     elemType.Init( elem, /*basicOnly=*/false );
11140     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11141
11142     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11143       newElem->setIsMarked( true );
11144   }
11145 }
11146
11147 //================================================================================
11148 /*!
11149   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11150   \param theElems - the list of elements (edges or faces) to be replicated
11151   The nodes for duplication could be found from these elements
11152   \param theNodesNot - list of nodes to NOT replicate
11153   \param theAffectedElems - the list of elements (cells and edges) to which the
11154   replicated nodes should be associated to.
11155   \return TRUE if operation has been completed successfully, FALSE otherwise
11156 */
11157 //================================================================================
11158
11159 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11160                                     const TIDSortedElemSet& theNodesNot,
11161                                     const TIDSortedElemSet& theAffectedElems )
11162 {
11163   ClearLastCreated();
11164
11165   if ( theElems.size() == 0 )
11166     return false;
11167
11168   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11169   if ( !aMeshDS )
11170     return false;
11171
11172   bool res = false;
11173   TNodeNodeMap anOldNodeToNewNode;
11174   // duplicate elements and nodes
11175   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11176   // replce nodes by duplications
11177   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11178   return res;
11179 }
11180
11181 //================================================================================
11182 /*!
11183   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11184   \param theMeshDS - mesh instance
11185   \param theElems - the elements replicated or modified (nodes should be changed)
11186   \param theNodesNot - nodes to NOT replicate
11187   \param theNodeNodeMap - relation of old node to new created node
11188   \param theIsDoubleElem - flag os to replicate element or modify
11189   \return TRUE if operation has been completed successfully, FALSE otherwise
11190 */
11191 //================================================================================
11192
11193 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11194                                    const TIDSortedElemSet& theElems,
11195                                    const TIDSortedElemSet& theNodesNot,
11196                                    TNodeNodeMap&           theNodeNodeMap,
11197                                    const bool              theIsDoubleElem )
11198 {
11199   // iterate through element and duplicate them (by nodes duplication)
11200   bool res = false;
11201   std::vector<const SMDS_MeshNode*> newNodes;
11202   ElemFeatures elemType;
11203
11204   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11205   for ( ;  elemItr != theElems.end(); ++elemItr )
11206   {
11207     const SMDS_MeshElement* anElem = *elemItr;
11208     // if (!anElem)
11209     //   continue;
11210
11211     // duplicate nodes to duplicate element
11212     bool isDuplicate = false;
11213     newNodes.resize( anElem->NbNodes() );
11214     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11215     int ind = 0;
11216     while ( anIter->more() )
11217     {
11218       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11219       const SMDS_MeshNode*  aNewNode = aCurrNode;
11220       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11221       if ( n2n != theNodeNodeMap.end() )
11222       {
11223         aNewNode = n2n->second;
11224       }
11225       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11226       {
11227         // duplicate node
11228         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11229         copyPosition( aCurrNode, aNewNode );
11230         theNodeNodeMap[ aCurrNode ] = aNewNode;
11231         myLastCreatedNodes.push_back( aNewNode );
11232       }
11233       isDuplicate |= (aCurrNode != aNewNode);
11234       newNodes[ ind++ ] = aNewNode;
11235     }
11236     if ( !isDuplicate )
11237       continue;
11238
11239     if ( theIsDoubleElem )
11240       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11241     else
11242     {
11243       // change element nodes
11244       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11245       if ( geomType == SMDSEntity_Polyhedra )
11246       {
11247         // special treatment for polyhedron
11248         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11249         if (!aPolyhedron) {
11250           MESSAGE("Warning: bad volumic element");
11251           return false;
11252         }
11253         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11254       }
11255       else
11256         // standard entity type
11257         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11258     }
11259
11260     res = true;
11261   }
11262   return res;
11263 }
11264
11265 //================================================================================
11266 /*!
11267   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11268   \param theNodes - identifiers of nodes to be doubled
11269   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11270   nodes. If list of element identifiers is empty then nodes are doubled but
11271   they not assigned to elements
11272   \return TRUE if operation has been completed successfully, FALSE otherwise
11273 */
11274 //================================================================================
11275
11276 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11277                                     const std::list< int >& theListOfModifiedElems )
11278 {
11279   ClearLastCreated();
11280
11281   if ( theListOfNodes.size() == 0 )
11282     return false;
11283
11284   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11285   if ( !aMeshDS )
11286     return false;
11287
11288   // iterate through nodes and duplicate them
11289
11290   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11291
11292   std::list< int >::const_iterator aNodeIter;
11293   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11294   {
11295     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11296     if ( !aNode )
11297       continue;
11298
11299     // duplicate node
11300
11301     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11302     if ( aNewNode )
11303     {
11304       copyPosition( aNode, aNewNode );
11305       anOldNodeToNewNode[ aNode ] = aNewNode;
11306       myLastCreatedNodes.push_back( aNewNode );
11307     }
11308   }
11309
11310   // Change nodes of elements
11311
11312   std::vector<const SMDS_MeshNode*> aNodeArr;
11313
11314   std::list< int >::const_iterator anElemIter;
11315   for ( anElemIter =  theListOfModifiedElems.begin();
11316         anElemIter != theListOfModifiedElems.end();
11317         anElemIter++ )
11318   {
11319     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11320     if ( !anElem )
11321       continue;
11322
11323     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11324     for( size_t i = 0; i < aNodeArr.size(); ++i )
11325     {
11326       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11327         anOldNodeToNewNode.find( aNodeArr[ i ]);
11328       if ( n2n != anOldNodeToNewNode.end() )
11329         aNodeArr[ i ] = n2n->second;
11330     }
11331     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11332   }
11333
11334   return true;
11335 }
11336
11337 namespace {
11338
11339   //================================================================================
11340   /*!
11341     \brief Check if element located inside shape
11342     \return TRUE if IN or ON shape, FALSE otherwise
11343   */
11344   //================================================================================
11345
11346   template<class Classifier>
11347   bool isInside(const SMDS_MeshElement* theElem,
11348                 Classifier&             theClassifier,
11349                 const double            theTol)
11350   {
11351     gp_XYZ centerXYZ (0, 0, 0);
11352     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11353       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11354
11355     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11356     theClassifier.Perform(aPnt, theTol);
11357     TopAbs_State aState = theClassifier.State();
11358     return (aState == TopAbs_IN || aState == TopAbs_ON );
11359   }
11360
11361   //================================================================================
11362   /*!
11363    * \brief Classifier of the 3D point on the TopoDS_Face
11364    *        with interaface suitable for isInside()
11365    */
11366   //================================================================================
11367
11368   struct _FaceClassifier
11369   {
11370     Extrema_ExtPS       _extremum;
11371     BRepAdaptor_Surface _surface;
11372     TopAbs_State        _state;
11373
11374     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11375     {
11376       _extremum.Initialize( _surface,
11377                             _surface.FirstUParameter(), _surface.LastUParameter(),
11378                             _surface.FirstVParameter(), _surface.LastVParameter(),
11379                             _surface.Tolerance(), _surface.Tolerance() );
11380     }
11381     void Perform(const gp_Pnt& aPnt, double theTol)
11382     {
11383       theTol *= theTol;
11384       _state = TopAbs_OUT;
11385       _extremum.Perform(aPnt);
11386       if ( _extremum.IsDone() )
11387         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11388           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11389     }
11390     TopAbs_State State() const
11391     {
11392       return _state;
11393     }
11394   };
11395 }
11396
11397 //================================================================================
11398 /*!
11399   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11400   This method is the first step of DoubleNodeElemGroupsInRegion.
11401   \param theElems - list of groups of elements (edges or faces) to be replicated
11402   \param theNodesNot - list of groups of nodes not to replicate
11403   \param theShape - shape to detect affected elements (element which geometric center
11404          located on or inside shape). If the shape is null, detection is done on faces orientations
11405          (select elements with a gravity center on the side given by faces normals).
11406          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11407          The replicated nodes should be associated to affected elements.
11408   \return true
11409   \sa DoubleNodeElemGroupsInRegion()
11410 */
11411 //================================================================================
11412
11413 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11414                                                    const TIDSortedElemSet& theNodesNot,
11415                                                    const TopoDS_Shape&     theShape,
11416                                                    TIDSortedElemSet&       theAffectedElems)
11417 {
11418   if ( theShape.IsNull() )
11419   {
11420     findAffectedElems( theElems, theAffectedElems );
11421   }
11422   else
11423   {
11424     const double aTol = Precision::Confusion();
11425     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11426     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11427     if ( theShape.ShapeType() == TopAbs_SOLID )
11428     {
11429       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11430       bsc3d->PerformInfinitePoint(aTol);
11431     }
11432     else if (theShape.ShapeType() == TopAbs_FACE )
11433     {
11434       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11435     }
11436
11437     // iterates on indicated elements and get elements by back references from their nodes
11438     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11439     for ( ;  elemItr != theElems.end(); ++elemItr )
11440     {
11441       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11442       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11443       while ( nodeItr->more() )
11444       {
11445         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11446         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11447           continue;
11448         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11449         while ( backElemItr->more() )
11450         {
11451           const SMDS_MeshElement* curElem = backElemItr->next();
11452           if ( curElem && theElems.find(curElem) == theElems.end() &&
11453                ( bsc3d.get() ?
11454                  isInside( curElem, *bsc3d, aTol ) :
11455                  isInside( curElem, *aFaceClassifier, aTol )))
11456             theAffectedElems.insert( curElem );
11457         }
11458       }
11459     }
11460   }
11461   return true;
11462 }
11463
11464 //================================================================================
11465 /*!
11466   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11467   \param theElems - group of of elements (edges or faces) to be replicated
11468   \param theNodesNot - group of nodes not to replicate
11469   \param theShape - shape to detect affected elements (element which geometric center
11470   located on or inside shape).
11471   The replicated nodes should be associated to affected elements.
11472   \return TRUE if operation has been completed successfully, FALSE otherwise
11473 */
11474 //================================================================================
11475
11476 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11477                                             const TIDSortedElemSet& theNodesNot,
11478                                             const TopoDS_Shape&     theShape )
11479 {
11480   if ( theShape.IsNull() )
11481     return false;
11482
11483   const double aTol = Precision::Confusion();
11484   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11485   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11486   if ( theShape.ShapeType() == TopAbs_SOLID )
11487   {
11488     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11489     bsc3d->PerformInfinitePoint(aTol);
11490   }
11491   else if (theShape.ShapeType() == TopAbs_FACE )
11492   {
11493     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11494   }
11495
11496   // iterates on indicated elements and get elements by back references from their nodes
11497   TIDSortedElemSet anAffected;
11498   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11499   for ( ;  elemItr != theElems.end(); ++elemItr )
11500   {
11501     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11502     if (!anElem)
11503       continue;
11504
11505     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11506     while ( nodeItr->more() )
11507     {
11508       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11509       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11510         continue;
11511       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11512       while ( backElemItr->more() )
11513       {
11514         const SMDS_MeshElement* curElem = backElemItr->next();
11515         if ( curElem && theElems.find(curElem) == theElems.end() &&
11516              ( bsc3d ?
11517                isInside( curElem, *bsc3d, aTol ) :
11518                isInside( curElem, *aFaceClassifier, aTol )))
11519           anAffected.insert( curElem );
11520       }
11521     }
11522   }
11523   return DoubleNodes( theElems, theNodesNot, anAffected );
11524 }
11525
11526 /*!
11527  *  \brief compute an oriented angle between two planes defined by four points.
11528  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11529  *  @param p0 base of the rotation axe
11530  *  @param p1 extremity of the rotation axe
11531  *  @param g1 belongs to the first plane
11532  *  @param g2 belongs to the second plane
11533  */
11534 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11535 {
11536   gp_Vec vref(p0, p1);
11537   gp_Vec v1(p0, g1);
11538   gp_Vec v2(p0, g2);
11539   gp_Vec n1 = vref.Crossed(v1);
11540   gp_Vec n2 = vref.Crossed(v2);
11541   try {
11542     return n2.AngleWithRef(n1, vref);
11543   }
11544   catch ( Standard_Failure& ) {
11545   }
11546   return Max( v1.Magnitude(), v2.Magnitude() );
11547 }
11548
11549 /*!
11550  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11551  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11552  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11553  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11554  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11555  * 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.
11556  * 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.
11557  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11558  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11559  * \param theElems - list of groups of volumes, where a group of volume is a set of
11560  *        SMDS_MeshElements sorted by Id.
11561  * \param createJointElems - if TRUE, create the elements
11562  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11563  *        the boundary between \a theDomains and the rest mesh
11564  * \return TRUE if operation has been completed successfully, FALSE otherwise
11565  */
11566 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11567                                                      bool                                 createJointElems,
11568                                                      bool                                 onAllBoundaries)
11569 {
11570   // MESSAGE("----------------------------------------------");
11571   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11572   // MESSAGE("----------------------------------------------");
11573
11574   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11575   meshDS->BuildDownWardConnectivity(true);
11576   CHRONO(50);
11577   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11578
11579   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11580   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11581   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11582
11583   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11584   std::map<int,int> celldom; // cell vtkId --> domain
11585   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11586   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11587
11588   //MESSAGE(".. Number of domains :"<<theElems.size());
11589
11590   TIDSortedElemSet theRestDomElems;
11591   const int iRestDom  = -1;
11592   const int idom0     = onAllBoundaries ? iRestDom : 0;
11593   const int nbDomains = theElems.size();
11594
11595   // Check if the domains do not share an element
11596   for (int idom = 0; idom < nbDomains-1; idom++)
11597   {
11598     //       MESSAGE("... Check of domain #" << idom);
11599     const TIDSortedElemSet& domain = theElems[idom];
11600     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11601     for (; elemItr != domain.end(); ++elemItr)
11602     {
11603       const SMDS_MeshElement* anElem = *elemItr;
11604       int idombisdeb = idom + 1 ;
11605       // check if the element belongs to a domain further in the list
11606       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11607       {
11608         const TIDSortedElemSet& domainbis = theElems[idombis];
11609         if ( domainbis.count( anElem ))
11610         {
11611           MESSAGE(".... Domain #" << idom);
11612           MESSAGE(".... Domain #" << idombis);
11613           throw SALOME_Exception("The domains are not disjoint.");
11614           return false ;
11615         }
11616       }
11617     }
11618   }
11619
11620   for (int idom = 0; idom < nbDomains; idom++)
11621   {
11622
11623     // --- build a map (face to duplicate --> volume to modify)
11624     //     with all the faces shared by 2 domains (group of elements)
11625     //     and corresponding volume of this domain, for each shared face.
11626     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11627
11628     //MESSAGE("... Neighbors of domain #" << idom);
11629     const TIDSortedElemSet& domain = theElems[idom];
11630     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11631     for (; elemItr != domain.end(); ++elemItr)
11632     {
11633       const SMDS_MeshElement* anElem = *elemItr;
11634       if (!anElem)
11635         continue;
11636       vtkIdType vtkId = anElem->GetVtkID();
11637       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11638       int neighborsVtkIds[NBMAXNEIGHBORS];
11639       int downIds[NBMAXNEIGHBORS];
11640       unsigned char downTypes[NBMAXNEIGHBORS];
11641       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11642       for (int n = 0; n < nbNeighbors; n++)
11643       {
11644         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11645         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11646         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11647         {
11648           bool ok = false;
11649           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11650           {
11651             // MESSAGE("Domain " << idombis);
11652             const TIDSortedElemSet& domainbis = theElems[idombis];
11653             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11654           }
11655           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11656           {
11657             DownIdType face(downIds[n], downTypes[n]);
11658             if (!faceDomains[face].count(idom))
11659             {
11660               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11661               celldom[vtkId] = idom;
11662               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11663             }
11664             if ( !ok )
11665             {
11666               theRestDomElems.insert( elem );
11667               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11668               celldom[neighborsVtkIds[n]] = iRestDom;
11669             }
11670           }
11671         }
11672       }
11673     }
11674   }
11675
11676   //MESSAGE("Number of shared faces " << faceDomains.size());
11677   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11678
11679   // --- explore the shared faces domain by domain,
11680   //     explore the nodes of the face and see if they belong to a cell in the domain,
11681   //     which has only a node or an edge on the border (not a shared face)
11682
11683   for (int idomain = idom0; idomain < nbDomains; idomain++)
11684   {
11685     //MESSAGE("Domain " << idomain);
11686     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11687     itface = faceDomains.begin();
11688     for (; itface != faceDomains.end(); ++itface)
11689     {
11690       const std::map<int, int>& domvol = itface->second;
11691       if (!domvol.count(idomain))
11692         continue;
11693       DownIdType face = itface->first;
11694       //MESSAGE(" --- face " << face.cellId);
11695       std::set<int> oldNodes;
11696       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11697       std::set<int>::iterator itn = oldNodes.begin();
11698       for (; itn != oldNodes.end(); ++itn)
11699       {
11700         int oldId = *itn;
11701         //MESSAGE("     node " << oldId);
11702         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11703         for (int i=0; i<l.ncells; i++)
11704         {
11705           int vtkId = l.cells[i];
11706           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11707           if (!domain.count(anElem))
11708             continue;
11709           int vtkType = grid->GetCellType(vtkId);
11710           int downId = grid->CellIdToDownId(vtkId);
11711           if (downId < 0)
11712           {
11713             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11714             continue; // not OK at this stage of the algorithm:
11715             //no cells created after BuildDownWardConnectivity
11716           }
11717           DownIdType aCell(downId, vtkType);
11718           cellDomains[aCell][idomain] = vtkId;
11719           celldom[vtkId] = idomain;
11720           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11721         }
11722       }
11723     }
11724   }
11725
11726   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11727   //     for each shared face, get the nodes
11728   //     for each node, for each domain of the face, create a clone of the node
11729
11730   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11731   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11732   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11733
11734   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11735   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11736   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11737
11738   //MESSAGE(".. Duplication of the nodes");
11739   for (int idomain = idom0; idomain < nbDomains; idomain++)
11740   {
11741     itface = faceDomains.begin();
11742     for (; itface != faceDomains.end(); ++itface)
11743     {
11744       const std::map<int, int>& domvol = itface->second;
11745       if (!domvol.count(idomain))
11746         continue;
11747       DownIdType face = itface->first;
11748       //MESSAGE(" --- face " << face.cellId);
11749       std::set<int> oldNodes;
11750       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11751       std::set<int>::iterator itn = oldNodes.begin();
11752       for (; itn != oldNodes.end(); ++itn)
11753       {
11754         int oldId = *itn;
11755         if (nodeDomains[oldId].empty())
11756         {
11757           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11758           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11759         }
11760         std::map<int, int>::const_iterator itdom = domvol.begin();
11761         for (; itdom != domvol.end(); ++itdom)
11762         {
11763           int idom = itdom->first;
11764           //MESSAGE("         domain " << idom);
11765           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11766           {
11767             if (nodeDomains[oldId].size() >= 2) // a multiple node
11768             {
11769               vector<int> orderedDoms;
11770               //MESSAGE("multiple node " << oldId);
11771               if (mutipleNodes.count(oldId))
11772                 orderedDoms = mutipleNodes[oldId];
11773               else
11774               {
11775                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11776                 for (; it != nodeDomains[oldId].end(); ++it)
11777                   orderedDoms.push_back(it->first);
11778               }
11779               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11780               //stringstream txt;
11781               //for (int i=0; i<orderedDoms.size(); i++)
11782               //  txt << orderedDoms[i] << " ";
11783               //MESSAGE("orderedDoms " << txt.str());
11784               mutipleNodes[oldId] = orderedDoms;
11785             }
11786             double *coords = grid->GetPoint(oldId);
11787             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11788             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11789             int newId = newNode->GetVtkID();
11790             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11791             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11792           }
11793         }
11794       }
11795     }
11796   }
11797
11798   //MESSAGE(".. Creation of elements");
11799   for (int idomain = idom0; idomain < nbDomains; idomain++)
11800   {
11801     itface = faceDomains.begin();
11802     for (; itface != faceDomains.end(); ++itface)
11803     {
11804       std::map<int, int> domvol = itface->second;
11805       if (!domvol.count(idomain))
11806         continue;
11807       DownIdType face = itface->first;
11808       //MESSAGE(" --- face " << face.cellId);
11809       std::set<int> oldNodes;
11810       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11811       int nbMultipleNodes = 0;
11812       std::set<int>::iterator itn = oldNodes.begin();
11813       for (; itn != oldNodes.end(); ++itn)
11814       {
11815         int oldId = *itn;
11816         if (mutipleNodes.count(oldId))
11817           nbMultipleNodes++;
11818       }
11819       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11820       {
11821         //MESSAGE("multiple Nodes detected on a shared face");
11822         int downId = itface->first.cellId;
11823         unsigned char cellType = itface->first.cellType;
11824         // --- shared edge or shared face ?
11825         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11826         {
11827           int nodes[3];
11828           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11829           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11830             if (mutipleNodes.count(nodes[i]))
11831               if (!mutipleNodesToFace.count(nodes[i]))
11832                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11833         }
11834         else // shared face (between two volumes)
11835         {
11836           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11837           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11838           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11839           for (int ie =0; ie < nbEdges; ie++)
11840           {
11841             int nodes[3];
11842             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11843             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11844             {
11845               vector<int> vn0 = mutipleNodes[nodes[0]];
11846               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11847               vector<int> doms;
11848               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11849                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11850                   if ( vn0[i0] == vn1[i1] )
11851                     doms.push_back( vn0[ i0 ]);
11852               if ( doms.size() > 2 )
11853               {
11854                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11855                 double *coords = grid->GetPoint(nodes[0]);
11856                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11857                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11858                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11859                 gp_Pnt gref;
11860                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11861                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11862                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11863                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11864                 for ( size_t id = 0; id < doms.size(); id++ )
11865                 {
11866                   int idom = doms[id];
11867                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11868                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11869                   {
11870                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11871                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11872                     if (domain.count(elem))
11873                     {
11874                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11875                       domvol[idom] = (SMDS_MeshVolume*) svol;
11876                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11877                       double values[3] = { 0,0,0 };
11878                       vtkIdType npts = 0;
11879                       vtkIdType const *pts(nullptr);
11880                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11881                       for ( vtkIdType i = 0; i < npts; ++i )
11882                       {
11883                         double *coords = grid->GetPoint( pts[i] );
11884                         for ( int j = 0; j < 3; ++j )
11885                           values[j] += coords[j] / npts;
11886                       }
11887                       if ( id == 0 )
11888                       {
11889                         gref.SetCoord( values[0], values[1], values[2] );
11890                         angleDom[idom] = 0;
11891                       }
11892                       else
11893                       {
11894                         gp_Pnt g( values[0], values[1], values[2] );
11895                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11896                         //MESSAGE("  angle=" << angleDom[idom]);
11897                       }
11898                       break;
11899                     }
11900                   }
11901                 }
11902                 map<double, int> sortedDom; // sort domains by angle
11903                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11904                   sortedDom[ia->second] = ia->first;
11905                 vector<int> vnodes;
11906                 vector<int> vdom;
11907                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11908                 {
11909                   vdom.push_back(ib->second);
11910                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11911                 }
11912                 for (int ino = 0; ino < nbNodes; ino++)
11913                   vnodes.push_back(nodes[ino]);
11914                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11915               }
11916             }
11917           }
11918         }
11919       }
11920     }
11921   }
11922
11923   // --- iterate on shared faces (volumes to modify, face to extrude)
11924   //     get node id's of the face (id SMDS = id VTK)
11925   //     create flat element with old and new nodes if requested
11926
11927   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11928   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11929
11930   std::map<int, std::map<long,int> > nodeQuadDomains;
11931   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11932
11933   //MESSAGE(".. Creation of elements: simple junction");
11934   if ( createJointElems )
11935   {
11936     string joints2DName = "joints2D";
11937     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11938     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11939     string joints3DName = "joints3D";
11940     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11941     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11942
11943     itface = faceDomains.begin();
11944     for (; itface != faceDomains.end(); ++itface)
11945     {
11946       DownIdType face = itface->first;
11947       std::set<int> oldNodes;
11948       std::set<int>::iterator itn;
11949       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11950
11951       std::map<int, int>          domvol = itface->second;
11952       std::map<int, int>::iterator itdom = domvol.begin();
11953       int     dom1 = itdom->first;
11954       int vtkVolId = itdom->second;
11955       itdom++;
11956       int           dom2 = itdom->first;
11957       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11958                                                        nodeQuadDomains);
11959       stringstream grpname;
11960       grpname << "j_";
11961       if (dom1 < dom2)
11962         grpname << dom1 << "_" << dom2;
11963       else
11964         grpname << dom2 << "_" << dom1;
11965       string namegrp = grpname.str();
11966       if (!mapOfJunctionGroups.count(namegrp))
11967         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11968       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11969       if (sgrp)
11970         sgrp->Add(vol->GetID());
11971       if (vol->GetType() == SMDSAbs_Volume)
11972         joints3DGrp->Add(vol->GetID());
11973       else if (vol->GetType() == SMDSAbs_Face)
11974         joints2DGrp->Add(vol->GetID());
11975     }
11976   }
11977
11978   // --- create volumes on multiple domain intersection if requested
11979   //     iterate on mutipleNodesToFace
11980   //     iterate on edgesMultiDomains
11981
11982   //MESSAGE(".. Creation of elements: multiple junction");
11983   if (createJointElems)
11984   {
11985     // --- iterate on mutipleNodesToFace
11986
11987     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11988     for (; itn != mutipleNodesToFace.end(); ++itn)
11989     {
11990       int node = itn->first;
11991       vector<int> orderDom = itn->second;
11992       vector<vtkIdType> orderedNodes;
11993       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11994         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11995       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11996
11997       stringstream grpname;
11998       grpname << "m2j_";
11999       grpname << 0 << "_" << 0;
12000       string namegrp = grpname.str();
12001       if (!mapOfJunctionGroups.count(namegrp))
12002         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
12003       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12004       if (sgrp)
12005         sgrp->Add(face->GetID());
12006     }
12007
12008     // --- iterate on edgesMultiDomains
12009
12010     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12011     for (; ite != edgesMultiDomains.end(); ++ite)
12012     {
12013       vector<int>    nodes = ite->first;
12014       vector<int> orderDom = ite->second;
12015       vector<vtkIdType> orderedNodes;
12016       if (nodes.size() == 2)
12017       {
12018         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12019         for ( size_t ino = 0; ino < nodes.size(); ino++ )
12020           if ( orderDom.size() == 3 )
12021             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12022               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12023           else
12024             for (int idom = orderDom.size()-1; idom >=0; idom--)
12025               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12026         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12027
12028         string namegrp = "jointsMultiples";
12029         if (!mapOfJunctionGroups.count(namegrp))
12030           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12031         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12032         if (sgrp)
12033           sgrp->Add(vol->GetID());
12034       }
12035       else
12036       {
12037         //INFOS("Quadratic multiple joints not implemented");
12038         // TODO quadratic nodes
12039       }
12040     }
12041   }
12042
12043   // --- list the explicit faces and edges of the mesh that need to be modified,
12044   //     i.e. faces and edges built with one or more duplicated nodes.
12045   //     associate these faces or edges to their corresponding domain.
12046   //     only the first domain found is kept when a face or edge is shared
12047
12048   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12049   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12050
12051   //MESSAGE(".. Modification of elements");
12052   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
12053   for (int idomain = idom0; idomain < nbDomains; idomain++)
12054   {
12055     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12056     for (; itnod != nodeDomains.end(); ++itnod)
12057     {
12058       int oldId = itnod->first;
12059       //MESSAGE("     node " << oldId);
12060       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
12061       for (int i = 0; i < l.ncells; i++)
12062       {
12063         int vtkId = l.cells[i];
12064         int vtkType = grid->GetCellType(vtkId);
12065         int downId = grid->CellIdToDownId(vtkId);
12066         if (downId < 0)
12067           continue; // new cells: not to be modified
12068         DownIdType aCell(downId, vtkType);
12069         int volParents[1000];
12070         int nbvol = 0;
12071         nbvol = grid->GetParentVolumes(volParents, vtkId);
12072         if ( domainType == SMDSAbs_Volume )
12073         {
12074           nbvol = grid->GetParentVolumes(volParents, vtkId);
12075         }
12076         else // domainType == SMDSAbs_Face
12077         {
12078           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
12079           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
12080           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
12081           for (int i=0; i< nbFaces; i++)
12082           {
12083             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
12084             if (vtkFaceId >= 0)
12085               volParents[nbvol++] = vtkFaceId;
12086           }
12087         }
12088         for (int j = 0; j < nbvol; j++)
12089           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12090             if (!feDom.count(vtkId))
12091             {
12092               feDom[vtkId] = idomain;
12093               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12094               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
12095               //        << " type " << vtkType << " downId " << downId);
12096             }
12097       }
12098     }
12099   }
12100
12101   // --- iterate on shared faces (volumes to modify, face to extrude)
12102   //     get node id's of the face
12103   //     replace old nodes by new nodes in volumes, and update inverse connectivity
12104
12105   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12106   for (int m=0; m<3; m++)
12107   {
12108     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12109     itface = (*amap).begin();
12110     for (; itface != (*amap).end(); ++itface)
12111     {
12112       DownIdType face = itface->first;
12113       std::set<int> oldNodes;
12114       std::set<int>::iterator itn;
12115       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12116       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12117       std::map<int, int> localClonedNodeIds;
12118
12119       std::map<int, int> domvol = itface->second;
12120       std::map<int, int>::iterator itdom = domvol.begin();
12121       for (; itdom != domvol.end(); ++itdom)
12122       {
12123         int idom = itdom->first;
12124         int vtkVolId = itdom->second;
12125         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12126         localClonedNodeIds.clear();
12127         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12128         {
12129           int oldId = *itn;
12130           if (nodeDomains[oldId].count(idom))
12131           {
12132             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12133             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12134           }
12135         }
12136         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12137       }
12138     }
12139   }
12140
12141   // Remove empty groups (issue 0022812)
12142   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12143   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12144   {
12145     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12146       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12147   }
12148
12149   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12150   grid->DeleteLinks();
12151
12152   CHRONOSTOP(50);
12153   counters::stats();
12154   return true;
12155 }
12156
12157 /*!
12158  * \brief Double nodes on some external faces and create flat elements.
12159  * Flat elements are mainly used by some types of mechanic calculations.
12160  *
12161  * Each group of the list must be constituted of faces.
12162  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12163  * @param theElems - list of groups of faces, where a group of faces is a set of
12164  * SMDS_MeshElements sorted by Id.
12165  * @return TRUE if operation has been completed successfully, FALSE otherwise
12166  */
12167 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12168 {
12169   // MESSAGE("-------------------------------------------------");
12170   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12171   // MESSAGE("-------------------------------------------------");
12172
12173   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12174
12175   // --- For each group of faces
12176   //     duplicate the nodes, create a flat element based on the face
12177   //     replace the nodes of the faces by their clones
12178
12179   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12180   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12181   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12182
12183   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12184   {
12185     const TIDSortedElemSet&           domain = theElems[idom];
12186     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12187     for ( ; elemItr != domain.end(); ++elemItr )
12188     {
12189       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12190       if (!aFace)
12191         continue;
12192       // MESSAGE("aFace=" << aFace->GetID());
12193       bool isQuad = aFace->IsQuadratic();
12194       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12195
12196       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12197
12198       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12199       while (nodeIt->more())
12200       {
12201         const SMDS_MeshNode* node = nodeIt->next();
12202         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12203         if (isMedium)
12204           ln2.push_back(node);
12205         else
12206           ln0.push_back(node);
12207
12208         const SMDS_MeshNode* clone = 0;
12209         if (!clonedNodes.count(node))
12210         {
12211           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12212           copyPosition( node, clone );
12213           clonedNodes[node] = clone;
12214         }
12215         else
12216           clone = clonedNodes[node];
12217
12218         if (isMedium)
12219           ln3.push_back(clone);
12220         else
12221           ln1.push_back(clone);
12222
12223         const SMDS_MeshNode* inter = 0;
12224         if (isQuad && (!isMedium))
12225         {
12226           if (!intermediateNodes.count(node))
12227           {
12228             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12229             copyPosition( node, inter );
12230             intermediateNodes[node] = inter;
12231           }
12232           else
12233             inter = intermediateNodes[node];
12234           ln4.push_back(inter);
12235         }
12236       }
12237
12238       // --- extrude the face
12239
12240       vector<const SMDS_MeshNode*> ln;
12241       SMDS_MeshVolume* vol = 0;
12242       vtkIdType aType = aFace->GetVtkType();
12243       switch (aType)
12244       {
12245       case VTK_TRIANGLE:
12246         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12247         // MESSAGE("vol prism " << vol->GetID());
12248         ln.push_back(ln1[0]);
12249         ln.push_back(ln1[1]);
12250         ln.push_back(ln1[2]);
12251         break;
12252       case VTK_QUAD:
12253         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12254         // MESSAGE("vol hexa " << vol->GetID());
12255         ln.push_back(ln1[0]);
12256         ln.push_back(ln1[1]);
12257         ln.push_back(ln1[2]);
12258         ln.push_back(ln1[3]);
12259         break;
12260       case VTK_QUADRATIC_TRIANGLE:
12261         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12262                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12263         // MESSAGE("vol quad prism " << vol->GetID());
12264         ln.push_back(ln1[0]);
12265         ln.push_back(ln1[1]);
12266         ln.push_back(ln1[2]);
12267         ln.push_back(ln3[0]);
12268         ln.push_back(ln3[1]);
12269         ln.push_back(ln3[2]);
12270         break;
12271       case VTK_QUADRATIC_QUAD:
12272         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12273         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12274         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12275         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12276                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12277                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12278         // MESSAGE("vol quad hexa " << vol->GetID());
12279         ln.push_back(ln1[0]);
12280         ln.push_back(ln1[1]);
12281         ln.push_back(ln1[2]);
12282         ln.push_back(ln1[3]);
12283         ln.push_back(ln3[0]);
12284         ln.push_back(ln3[1]);
12285         ln.push_back(ln3[2]);
12286         ln.push_back(ln3[3]);
12287         break;
12288       case VTK_POLYGON:
12289         break;
12290       default:
12291         break;
12292       }
12293
12294       if (vol)
12295       {
12296         stringstream grpname;
12297         grpname << "jf_";
12298         grpname << idom;
12299         string namegrp = grpname.str();
12300         if (!mapOfJunctionGroups.count(namegrp))
12301           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12302         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12303         if (sgrp)
12304           sgrp->Add(vol->GetID());
12305       }
12306
12307       // --- modify the face
12308
12309       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12310     }
12311   }
12312   return true;
12313 }
12314
12315 /*!
12316  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12317  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12318  *  groups of faces to remove inside the object, (idem edges).
12319  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12320  */
12321 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12322                                       const TopoDS_Shape&             theShape,
12323                                       SMESH_NodeSearcher*             theNodeSearcher,
12324                                       const char*                     groupName,
12325                                       std::vector<double>&            nodesCoords,
12326                                       std::vector<std::vector<int> >& listOfListOfNodes)
12327 {
12328   // MESSAGE("--------------------------------");
12329   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12330   // MESSAGE("--------------------------------");
12331
12332   // --- zone of volumes to remove is given :
12333   //     1 either by a geom shape (one or more vertices) and a radius,
12334   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12335   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12336   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12337   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12338   //     defined by it's name.
12339
12340   SMESHDS_GroupBase* groupDS = 0;
12341   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12342   while ( groupIt->more() )
12343   {
12344     groupDS = 0;
12345     SMESH_Group * group = groupIt->next();
12346     if ( !group ) continue;
12347     groupDS = group->GetGroupDS();
12348     if ( !groupDS || groupDS->IsEmpty() ) continue;
12349     std::string grpName = group->GetName();
12350     //MESSAGE("grpName=" << grpName);
12351     if (grpName == groupName)
12352       break;
12353     else
12354       groupDS = 0;
12355   }
12356
12357   bool isNodeGroup = false;
12358   bool isNodeCoords = false;
12359   if (groupDS)
12360   {
12361     if (groupDS->GetType() != SMDSAbs_Node)
12362       return;
12363     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12364   }
12365
12366   if (nodesCoords.size() > 0)
12367     isNodeCoords = true; // a list o nodes given by their coordinates
12368   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12369
12370   // --- define groups to build
12371
12372   // --- group of SMDS volumes
12373   string grpvName = groupName;
12374   grpvName += "_vol";
12375   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12376   if (!grp)
12377   {
12378     MESSAGE("group not created " << grpvName);
12379     return;
12380   }
12381   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12382
12383   // --- group of SMDS faces on the skin
12384   string grpsName = groupName;
12385   grpsName += "_skin";
12386   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12387   if (!grps)
12388   {
12389     MESSAGE("group not created " << grpsName);
12390     return;
12391   }
12392   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12393
12394   // --- group of SMDS faces internal (several shapes)
12395   string grpiName = groupName;
12396   grpiName += "_internalFaces";
12397   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12398   if (!grpi)
12399   {
12400     MESSAGE("group not created " << grpiName);
12401     return;
12402   }
12403   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12404
12405   // --- group of SMDS faces internal (several shapes)
12406   string grpeiName = groupName;
12407   grpeiName += "_internalEdges";
12408   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12409   if (!grpei)
12410   {
12411     MESSAGE("group not created " << grpeiName);
12412     return;
12413   }
12414   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12415
12416   // --- build downward connectivity
12417
12418   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12419   meshDS->BuildDownWardConnectivity(true);
12420   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12421
12422   // --- set of volumes detected inside
12423
12424   std::set<int> setOfInsideVol;
12425   std::set<int> setOfVolToCheck;
12426
12427   std::vector<gp_Pnt> gpnts;
12428
12429   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12430   {
12431     //MESSAGE("group of nodes provided");
12432     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12433     while ( elemIt->more() )
12434     {
12435       const SMDS_MeshElement* elem = elemIt->next();
12436       if (!elem)
12437         continue;
12438       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12439       if (!node)
12440         continue;
12441       SMDS_MeshElement* vol = 0;
12442       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12443       while (volItr->more())
12444       {
12445         vol = (SMDS_MeshElement*)volItr->next();
12446         setOfInsideVol.insert(vol->GetVtkID());
12447         sgrp->Add(vol->GetID());
12448       }
12449     }
12450   }
12451   else if (isNodeCoords)
12452   {
12453     //MESSAGE("list of nodes coordinates provided");
12454     size_t i = 0;
12455     int k = 0;
12456     while ( i < nodesCoords.size()-2 )
12457     {
12458       double x = nodesCoords[i++];
12459       double y = nodesCoords[i++];
12460       double z = nodesCoords[i++];
12461       gp_Pnt p = gp_Pnt(x, y ,z);
12462       gpnts.push_back(p);
12463       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12464       k++;
12465     }
12466   }
12467   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12468   {
12469     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12470     TopTools_IndexedMapOfShape vertexMap;
12471     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12472     gp_Pnt p = gp_Pnt(0,0,0);
12473     if (vertexMap.Extent() < 1)
12474       return;
12475
12476     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12477     {
12478       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12479       p = BRep_Tool::Pnt(vertex);
12480       gpnts.push_back(p);
12481       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12482     }
12483   }
12484
12485   if (gpnts.size() > 0)
12486   {
12487     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12488     //MESSAGE("startNode->nodeId " << nodeId);
12489
12490     double radius2 = radius*radius;
12491     //MESSAGE("radius2 " << radius2);
12492
12493     // --- volumes on start node
12494
12495     setOfVolToCheck.clear();
12496     SMDS_MeshElement* startVol = 0;
12497     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12498     while (volItr->more())
12499     {
12500       startVol = (SMDS_MeshElement*)volItr->next();
12501       setOfVolToCheck.insert(startVol->GetVtkID());
12502     }
12503     if (setOfVolToCheck.empty())
12504     {
12505       MESSAGE("No volumes found");
12506       return;
12507     }
12508
12509     // --- starting with central volumes then their neighbors, check if they are inside
12510     //     or outside the domain, until no more new neighbor volume is inside.
12511     //     Fill the group of inside volumes
12512
12513     std::map<int, double> mapOfNodeDistance2;
12514     std::set<int> setOfOutsideVol;
12515     while (!setOfVolToCheck.empty())
12516     {
12517       std::set<int>::iterator it = setOfVolToCheck.begin();
12518       int vtkId = *it;
12519       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12520       bool volInside = false;
12521       vtkIdType npts = 0;
12522       vtkIdType const *pts(nullptr);
12523       grid->GetCellPoints(vtkId, npts, pts);
12524       for (int i=0; i<npts; i++)
12525       {
12526         double distance2 = 0;
12527         if (mapOfNodeDistance2.count(pts[i]))
12528         {
12529           distance2 = mapOfNodeDistance2[pts[i]];
12530           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12531         }
12532         else
12533         {
12534           double *coords = grid->GetPoint(pts[i]);
12535           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12536           distance2 = 1.E40;
12537           for ( size_t j = 0; j < gpnts.size(); j++ )
12538           {
12539             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12540             if (d2 < distance2)
12541             {
12542               distance2 = d2;
12543               if (distance2 < radius2)
12544                 break;
12545             }
12546           }
12547           mapOfNodeDistance2[pts[i]] = distance2;
12548           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12549         }
12550         if (distance2 < radius2)
12551         {
12552           volInside = true; // one or more nodes inside the domain
12553           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12554           break;
12555         }
12556       }
12557       if (volInside)
12558       {
12559         setOfInsideVol.insert(vtkId);
12560         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12561         int neighborsVtkIds[NBMAXNEIGHBORS];
12562         int downIds[NBMAXNEIGHBORS];
12563         unsigned char downTypes[NBMAXNEIGHBORS];
12564         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12565         for (int n = 0; n < nbNeighbors; n++)
12566           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12567             setOfVolToCheck.insert(neighborsVtkIds[n]);
12568       }
12569       else
12570       {
12571         setOfOutsideVol.insert(vtkId);
12572         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12573       }
12574       setOfVolToCheck.erase(vtkId);
12575     }
12576   }
12577
12578   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12579   //     If yes, add the volume to the inside set
12580
12581   bool addedInside = true;
12582   std::set<int> setOfVolToReCheck;
12583   while (addedInside)
12584   {
12585     //MESSAGE(" --------------------------- re check");
12586     addedInside = false;
12587     std::set<int>::iterator itv = setOfInsideVol.begin();
12588     for (; itv != setOfInsideVol.end(); ++itv)
12589     {
12590       int vtkId = *itv;
12591       int neighborsVtkIds[NBMAXNEIGHBORS];
12592       int downIds[NBMAXNEIGHBORS];
12593       unsigned char downTypes[NBMAXNEIGHBORS];
12594       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12595       for (int n = 0; n < nbNeighbors; n++)
12596         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12597           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12598     }
12599     setOfVolToCheck = setOfVolToReCheck;
12600     setOfVolToReCheck.clear();
12601     while  (!setOfVolToCheck.empty())
12602     {
12603       std::set<int>::iterator it = setOfVolToCheck.begin();
12604       int vtkId = *it;
12605       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12606       {
12607         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12608         int countInside = 0;
12609         int neighborsVtkIds[NBMAXNEIGHBORS];
12610         int downIds[NBMAXNEIGHBORS];
12611         unsigned char downTypes[NBMAXNEIGHBORS];
12612         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12613         for (int n = 0; n < nbNeighbors; n++)
12614           if (setOfInsideVol.count(neighborsVtkIds[n]))
12615             countInside++;
12616         //MESSAGE("countInside " << countInside);
12617         if (countInside > 1)
12618         {
12619           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12620           setOfInsideVol.insert(vtkId);
12621           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12622           addedInside = true;
12623         }
12624         else
12625           setOfVolToReCheck.insert(vtkId);
12626       }
12627       setOfVolToCheck.erase(vtkId);
12628     }
12629   }
12630
12631   // --- map of Downward faces at the boundary, inside the global volume
12632   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12633   //     fill group of SMDS faces inside the volume (when several volume shapes)
12634   //     fill group of SMDS faces on the skin of the global volume (if skin)
12635
12636   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12637   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12638   std::set<int>::iterator it = setOfInsideVol.begin();
12639   for (; it != setOfInsideVol.end(); ++it)
12640   {
12641     int vtkId = *it;
12642     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12643     int neighborsVtkIds[NBMAXNEIGHBORS];
12644     int downIds[NBMAXNEIGHBORS];
12645     unsigned char downTypes[NBMAXNEIGHBORS];
12646     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12647     for (int n = 0; n < nbNeighbors; n++)
12648     {
12649       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12650       if (neighborDim == 3)
12651       {
12652         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12653         {
12654           DownIdType face(downIds[n], downTypes[n]);
12655           boundaryFaces[face] = vtkId;
12656         }
12657         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12658         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12659         if (vtkFaceId >= 0)
12660         {
12661           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12662           // find also the smds edges on this face
12663           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12664           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12665           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12666           for (int i = 0; i < nbEdges; i++)
12667           {
12668             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12669             if (vtkEdgeId >= 0)
12670               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12671           }
12672         }
12673       }
12674       else if (neighborDim == 2) // skin of the volume
12675       {
12676         DownIdType face(downIds[n], downTypes[n]);
12677         skinFaces[face] = vtkId;
12678         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12679         if (vtkFaceId >= 0)
12680           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12681       }
12682     }
12683   }
12684
12685   // --- identify the edges constituting the wire of each subshape on the skin
12686   //     define polylines with the nodes of edges, equivalent to wires
12687   //     project polylines on subshapes, and partition, to get geom faces
12688
12689   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12690   std::set<int>                 shapeIds;
12691
12692   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12693   while (itelem->more())
12694   {
12695     const SMDS_MeshElement *elem = itelem->next();
12696     int shapeId = elem->getshapeId();
12697     int   vtkId = elem->GetVtkID();
12698     if (!shapeIdToVtkIdSet.count(shapeId))
12699     {
12700       shapeIds.insert(shapeId);
12701     }
12702     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12703   }
12704
12705   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12706   std::set<DownIdType, DownIdCompare> emptyEdges;
12707
12708   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12709   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12710   {
12711     int shapeId = itShape->first;
12712     //MESSAGE(" --- Shape ID --- "<< shapeId);
12713     shapeIdToEdges[shapeId] = emptyEdges;
12714
12715     std::vector<int> nodesEdges;
12716
12717     std::set<int>::iterator its = itShape->second.begin();
12718     for (; its != itShape->second.end(); ++its)
12719     {
12720       int vtkId = *its;
12721       //MESSAGE("     " << vtkId);
12722       int neighborsVtkIds[NBMAXNEIGHBORS];
12723       int downIds[NBMAXNEIGHBORS];
12724       unsigned char downTypes[NBMAXNEIGHBORS];
12725       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12726       for (int n = 0; n < nbNeighbors; n++)
12727       {
12728         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12729           continue;
12730         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12731         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12732         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12733         {
12734           DownIdType edge(downIds[n], downTypes[n]);
12735           if (!shapeIdToEdges[shapeId].count(edge))
12736           {
12737             shapeIdToEdges[shapeId].insert(edge);
12738             int vtkNodeId[3];
12739             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12740             nodesEdges.push_back(vtkNodeId[0]);
12741             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12742             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12743           }
12744         }
12745       }
12746     }
12747
12748     std::list<int> order;
12749     if (nodesEdges.size() > 0)
12750     {
12751       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12752       nodesEdges[0] = -1;
12753       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12754       nodesEdges[1] = -1; // do not reuse this edge
12755       bool found = true;
12756       while (found)
12757       {
12758         int nodeTofind = order.back(); // try first to push back
12759         int i = 0;
12760         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12761           if (nodesEdges[i] == nodeTofind)
12762             break;
12763         if ( i == (int) nodesEdges.size() )
12764           found = false; // no follower found on back
12765         else
12766         {
12767           if (i%2) // odd ==> use the previous one
12768             if (nodesEdges[i-1] < 0)
12769               found = false;
12770             else
12771             {
12772               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12773               nodesEdges[i-1] = -1;
12774             }
12775           else // even ==> use the next one
12776             if (nodesEdges[i+1] < 0)
12777               found = false;
12778             else
12779             {
12780               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12781               nodesEdges[i+1] = -1;
12782             }
12783         }
12784         if (found)
12785           continue;
12786         // try to push front
12787         found = true;
12788         nodeTofind = order.front(); // try to push front
12789         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12790           if ( nodesEdges[i] == nodeTofind )
12791             break;
12792         if ( i == (int)nodesEdges.size() )
12793         {
12794           found = false; // no predecessor found on front
12795           continue;
12796         }
12797         if (i%2) // odd ==> use the previous one
12798           if (nodesEdges[i-1] < 0)
12799             found = false;
12800           else
12801           {
12802             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12803             nodesEdges[i-1] = -1;
12804           }
12805         else // even ==> use the next one
12806           if (nodesEdges[i+1] < 0)
12807             found = false;
12808           else
12809           {
12810             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12811             nodesEdges[i+1] = -1;
12812           }
12813       }
12814     }
12815
12816
12817     std::vector<int> nodes;
12818     nodes.push_back(shapeId);
12819     std::list<int>::iterator itl = order.begin();
12820     for (; itl != order.end(); itl++)
12821     {
12822       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12823       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12824     }
12825     listOfListOfNodes.push_back(nodes);
12826   }
12827
12828   //     partition geom faces with blocFissure
12829   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12830   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12831
12832   return;
12833 }
12834
12835
12836 //================================================================================
12837 /*!
12838  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12839  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12840  * \return TRUE if operation has been completed successfully, FALSE otherwise
12841  */
12842 //================================================================================
12843
12844 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12845 {
12846   // iterates on volume elements and detect all free faces on them
12847   SMESHDS_Mesh* aMesh = GetMeshDS();
12848   if (!aMesh)
12849     return false;
12850
12851   ElemFeatures faceType( SMDSAbs_Face );
12852   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12853   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12854   while(vIt->more())
12855   {
12856     const SMDS_MeshVolume* volume = vIt->next();
12857     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12858     vTool.SetExternalNormal();
12859     const int iQuad = volume->IsQuadratic();
12860     faceType.SetQuad( iQuad );
12861     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12862     {
12863       if (!vTool.IsFreeFace(iface))
12864         continue;
12865       nbFree++;
12866       vector<const SMDS_MeshNode *> nodes;
12867       int nbFaceNodes = vTool.NbFaceNodes(iface);
12868       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12869       int inode = 0;
12870       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12871         nodes.push_back(faceNodes[inode]);
12872
12873       if (iQuad) // add medium nodes
12874       {
12875         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12876           nodes.push_back(faceNodes[inode]);
12877         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12878           nodes.push_back(faceNodes[8]);
12879       }
12880       // add new face based on volume nodes
12881       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12882       {
12883         nbExisted++; // face already exists
12884       }
12885       else
12886       {
12887         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12888         nbCreated++;
12889       }
12890     }
12891   }
12892   return ( nbFree == ( nbExisted + nbCreated ));
12893 }
12894
12895 namespace
12896 {
12897   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12898   {
12899     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12900       return n;
12901     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12902   }
12903 }
12904 //================================================================================
12905 /*!
12906  * \brief Creates missing boundary elements
12907  *  \param elements - elements whose boundary is to be checked
12908  *  \param dimension - defines type of boundary elements to create
12909  *  \param group - a group to store created boundary elements in
12910  *  \param targetMesh - a mesh to store created boundary elements in
12911  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12912  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12913  *                                boundary elements will be copied into the targetMesh
12914  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12915  *                                boundary elements will be added into the new group
12916  *  \param aroundElements - if true, elements will be created on boundary of given
12917  *                          elements else, on boundary of the whole mesh.
12918  * \return nb of added boundary elements
12919  */
12920 //================================================================================
12921
12922 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12923                                        Bnd_Dimension           dimension,
12924                                        SMESH_Group*            group/*=0*/,
12925                                        SMESH_Mesh*             targetMesh/*=0*/,
12926                                        bool                    toCopyElements/*=false*/,
12927                                        bool                    toCopyExistingBoundary/*=false*/,
12928                                        bool                    toAddExistingBondary/*= false*/,
12929                                        bool                    aroundElements/*= false*/,
12930                                        bool                    toCreateAllElements/*= false*/)
12931 {
12932   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12933   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12934   // hope that all elements are of the same type, do not check them all
12935   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12936     throw SALOME_Exception(LOCALIZED("wrong element type"));
12937
12938   if ( !targetMesh )
12939     toCopyElements = toCopyExistingBoundary = false;
12940
12941   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12942   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12943   int nbAddedBnd = 0;
12944
12945   // editor adding present bnd elements and optionally holding elements to add to the group
12946   SMESH_MeshEditor* presentEditor;
12947   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12948   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12949   SMESH_MesherHelper helper( *myMesh );
12950   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12951   SMDS_VolumeTool vTool;
12952   TIDSortedElemSet avoidSet;
12953   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12954   size_t inode;
12955
12956   typedef vector<const SMDS_MeshNode*> TConnectivity;
12957   TConnectivity tgtNodes;
12958   ElemFeatures elemKind( missType ), elemToCopy;
12959
12960   vector<const SMDS_MeshElement*> presentBndElems;
12961   vector<TConnectivity>           missingBndElems;
12962   vector<int>                     freeFacets;
12963   TConnectivity nodes, elemNodes;
12964
12965   SMDS_ElemIteratorPtr eIt;
12966   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12967   else                  eIt = SMESHUtils::elemSetIterator( elements );
12968
12969   while ( eIt->more() )
12970   {
12971     const SMDS_MeshElement* elem = eIt->next();
12972     const int              iQuad = elem->IsQuadratic();
12973     elemKind.SetQuad( iQuad );
12974
12975     // ------------------------------------------------------------------------------------
12976     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12977     // ------------------------------------------------------------------------------------
12978     presentBndElems.clear();
12979     missingBndElems.clear();
12980     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12981     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12982     {
12983       const SMDS_MeshElement* otherVol = 0;
12984       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12985       {
12986         if ( !toCreateAllElements && 
12987               !vTool.IsFreeFace(iface, &otherVol) &&
12988                 ( !aroundElements || elements.count( otherVol )))
12989           continue;
12990         freeFacets.push_back( iface );
12991       }
12992       if ( missType == SMDSAbs_Face )
12993         vTool.SetExternalNormal();
12994       for ( size_t i = 0; i < freeFacets.size(); ++i )
12995       {
12996         int                iface = freeFacets[i];
12997         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12998         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12999         if ( missType == SMDSAbs_Edge ) // boundary edges
13000         {
13001           nodes.resize( 2+iQuad );
13002           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13003           {
13004             for ( size_t j = 0; j < nodes.size(); ++j )
13005               nodes[ j ] = nn[ i+j ];
13006             if ( const SMDS_MeshElement* edge =
13007                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13008               presentBndElems.push_back( edge );
13009             else
13010               missingBndElems.push_back( nodes );
13011           }
13012         }
13013         else // boundary face
13014         {
13015           nodes.clear();
13016           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13017             nodes.push_back( nn[inode] ); // add corner nodes
13018           if (iQuad)
13019             for ( inode = 1; inode < nbFaceNodes; inode += 2)
13020               nodes.push_back( nn[inode] ); // add medium nodes
13021           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13022           if ( iCenter > 0 )
13023             nodes.push_back( vTool.GetNodes()[ iCenter ] );
13024
13025           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13026                                                                SMDSAbs_Face, /*noMedium=*/false ))
13027             presentBndElems.push_back( f );
13028           else
13029             missingBndElems.push_back( nodes );
13030
13031           if ( targetMesh != myMesh )
13032           {
13033             // add 1D elements on face boundary to be added to a new mesh
13034             const SMDS_MeshElement* edge;
13035             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13036             {
13037               if ( iQuad )
13038                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13039               else
13040                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13041               if ( edge && avoidSet.insert( edge ).second )
13042                 presentBndElems.push_back( edge );
13043             }
13044           }
13045         }
13046       }
13047     }
13048     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13049     {
13050       avoidSet.clear(), avoidSet.insert( elem );
13051       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
13052                         SMDS_MeshElement::iterator() );
13053       elemNodes.push_back( elemNodes[0] );
13054       nodes.resize( 2 + iQuad );
13055       const int nbLinks = elem->NbCornerNodes();
13056       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13057       {
13058         nodes[0] = elemNodes[iN];
13059         nodes[1] = elemNodes[iN+1+iQuad];
13060         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13061           continue; // not free link
13062
13063         if ( iQuad ) nodes[2] = elemNodes[iN+1];
13064         if ( const SMDS_MeshElement* edge =
13065              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13066           presentBndElems.push_back( edge );
13067         else
13068           missingBndElems.push_back( nodes );
13069       }
13070     }
13071
13072     // ---------------------------------
13073     // 2. Add missing boundary elements
13074     // ---------------------------------
13075     if ( targetMesh != myMesh )
13076       // instead of making a map of nodes in this mesh and targetMesh,
13077       // we create nodes with same IDs.
13078       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13079       {
13080         TConnectivity& srcNodes = missingBndElems[i];
13081         tgtNodes.resize( srcNodes.size() );
13082         for ( inode = 0; inode < srcNodes.size(); ++inode )
13083           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13084         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13085                                                                        missType,
13086                                                                        /*noMedium=*/false))
13087           continue;
13088         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13089         ++nbAddedBnd;
13090       }
13091     else
13092       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13093       {
13094         TConnectivity& nodes = missingBndElems[ i ];
13095         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
13096                                                                        missType,
13097                                                                        /*noMedium=*/false))
13098           continue;
13099         SMDS_MeshElement* newElem =
13100           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13101         nbAddedBnd += bool( newElem );
13102
13103         // try to set a new element to a shape
13104         if ( myMesh->HasShapeToMesh() )
13105         {
13106           bool ok = true;
13107           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13108           const size_t nbN = nodes.size() / (iQuad+1 );
13109           for ( inode = 0; inode < nbN && ok; ++inode )
13110           {
13111             pair<int, TopAbs_ShapeEnum> i_stype =
13112               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13113             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13114               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13115           }
13116           if ( ok && mediumShapes.size() > 1 )
13117           {
13118             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13119             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13120             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13121             {
13122               if (( ok = ( stype_i->first != stype_i_0.first )))
13123                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13124                                         aMesh->IndexToShape( stype_i_0.second ));
13125             }
13126           }
13127           if ( ok && mediumShapes.begin()->first == missShapeType )
13128             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13129         }
13130       }
13131
13132     // ----------------------------------
13133     // 3. Copy present boundary elements
13134     // ----------------------------------
13135     if ( toCopyExistingBoundary )
13136       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13137       {
13138         const SMDS_MeshElement* e = presentBndElems[i];
13139         tgtNodes.resize( e->NbNodes() );
13140         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13141           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13142         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13143       }
13144     else // store present elements to add them to a group
13145       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13146       {
13147         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13148       }
13149
13150   } // loop on given elements
13151
13152   // ---------------------------------------------
13153   // 4. Fill group with boundary elements
13154   // ---------------------------------------------
13155   if ( group )
13156   {
13157     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13158       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13159         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13160   }
13161   tgtEditor.myLastCreatedElems.clear();
13162   tgtEditor2.myLastCreatedElems.clear();
13163
13164   // -----------------------
13165   // 5. Copy given elements
13166   // -----------------------
13167   if ( toCopyElements && targetMesh != myMesh )
13168   {
13169     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13170     else                  eIt = SMESHUtils::elemSetIterator( elements );
13171     while (eIt->more())
13172     {
13173       const SMDS_MeshElement* elem = eIt->next();
13174       tgtNodes.resize( elem->NbNodes() );
13175       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13176         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13177       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13178
13179       tgtEditor.myLastCreatedElems.clear();
13180     }
13181   }
13182   return nbAddedBnd;
13183 }
13184
13185 //================================================================================
13186 /*!
13187  * \brief Copy node position and set \a to node on the same geometry
13188  */
13189 //================================================================================
13190
13191 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13192                                      const SMDS_MeshNode* to )
13193 {
13194   if ( !from || !to ) return;
13195
13196   SMDS_PositionPtr pos = from->GetPosition();
13197   if ( !pos || from->getshapeId() < 1 ) return;
13198
13199   switch ( pos->GetTypeOfPosition() )
13200   {
13201   case SMDS_TOP_3DSPACE: break;
13202
13203   case SMDS_TOP_FACE:
13204   {
13205     SMDS_FacePositionPtr fPos = pos;
13206     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13207                                 fPos->GetUParameter(), fPos->GetVParameter() );
13208     break;
13209   }
13210   case SMDS_TOP_EDGE:
13211   {
13212     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13213     SMDS_EdgePositionPtr ePos = pos;
13214     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13215     break;
13216   }
13217   case SMDS_TOP_VERTEX:
13218   {
13219     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13220     break;
13221   }
13222   case SMDS_TOP_UNSPEC:
13223   default:;
13224   }
13225 }