Salome HOME
125bbaa2e04c031916233c38deb0fe52ee32ab03
[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 #include <Basics_OCCTVersion.hxx>
107
108 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
109
110 using namespace std;
111 using namespace SMESH::Controls;
112
113 //=======================================================================
114 //function : SMESH_MeshEditor
115 //purpose  :
116 //=======================================================================
117
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119   :myMesh( theMesh ) // theMesh may be NULL
120 {
121 }
122
123 //================================================================================
124 /*!
125  * \brief Return mesh DS
126  */
127 //================================================================================
128
129 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
130 {
131   return myMesh->GetMeshDS();
132 }
133
134
135 //================================================================================
136 /*!
137  * \brief Clears myLastCreatedNodes and myLastCreatedElems
138  */
139 //================================================================================
140
141 void SMESH_MeshEditor::ClearLastCreated()
142 {
143   SMESHUtils::FreeVector( myLastCreatedElems );
144   SMESHUtils::FreeVector( myLastCreatedNodes );
145 }
146
147 //================================================================================
148 /*!
149  * \brief Initializes members by an existing element
150  *  \param [in] elem - the source element
151  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
152  */
153 //================================================================================
154
155 SMESH_MeshEditor::ElemFeatures&
156 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
157 {
158   if ( elem )
159   {
160     myType = elem->GetType();
161     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
162     {
163       myIsPoly = elem->IsPoly();
164       if ( myIsPoly )
165       {
166         myIsQuad = elem->IsQuadratic();
167         if ( myType == SMDSAbs_Volume && !basicOnly )
168         {
169           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
170         }
171       }
172     }
173     else if ( myType == SMDSAbs_Ball && !basicOnly )
174     {
175       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
176     }
177   }
178   return *this;
179 }
180
181 //=======================================================================
182 /*!
183  * \brief Add element
184  */
185 //=======================================================================
186
187 SMDS_MeshElement*
188 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
189                              const ElemFeatures&                  features)
190 {
191   SMDS_MeshElement* e = 0;
192   int nbnode = node.size();
193   SMESHDS_Mesh* mesh = GetMeshDS();
194   const smIdType ID = features.myID;
195
196   switch ( features.myType ) {
197   case SMDSAbs_Face:
198     if ( !features.myIsPoly ) {
199       if      (nbnode == 3) {
200         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
201         else           e = mesh->AddFace      (node[0], node[1], node[2] );
202       }
203       else if (nbnode == 4) {
204         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
206       }
207       else if (nbnode == 6) {
208         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209                                                node[4], node[5], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
211                                                node[4], node[5] );
212       }
213       else if (nbnode == 7) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], node[6], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6] );
218       }
219       else if (nbnode == 8) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], node[7], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7] );
224       }
225       else if (nbnode == 9) {
226         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7], node[8], ID);
228         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7], node[8] );
230       }
231     }
232     else if ( !features.myIsQuad )
233     {
234       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
235       else           e = mesh->AddPolygonalFace      (node    );
236     }
237     else if ( nbnode % 2 == 0 ) // just a protection
238     {
239       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
240       else           e = mesh->AddQuadPolygonalFace      (node    );
241     }
242     break;
243
244   case SMDSAbs_Volume:
245     if ( !features.myIsPoly ) {
246       if      (nbnode == 4) {
247         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
248         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
249       }
250       else if (nbnode == 5) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4] );
255       }
256       else if (nbnode == 6) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], node[5], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4], node[5] );
261       }
262       else if (nbnode == 8) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7], ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7] );
267       }
268       else if (nbnode == 10) {
269         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9], ID);
272         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9] );
275       }
276       else if (nbnode == 12) {
277         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11], ID);
280         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
281                                                  node[4], node[5], node[6], node[7],
282                                                  node[8], node[9], node[10], node[11] );
283       }
284       else if (nbnode == 13) {
285         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10],node[11],
288                                                  node[12],ID);
289         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
290                                                  node[4], node[5], node[6], node[7],
291                                                  node[8], node[9], node[10],node[11],
292                                                  node[12] );
293       }
294       else if (nbnode == 15) {
295         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12],node[13],node[14],ID);
299         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
300                                                  node[4], node[5], node[6], node[7],
301                                                  node[8], node[9], node[10],node[11],
302                                                  node[12],node[13],node[14] );
303       }
304       else if (nbnode == 18) {
305         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14],
309                                                  node[15],node[16],node[17],ID );
310         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
311                                                  node[4], node[5], node[6], node[7],
312                                                  node[8], node[9], node[10],node[11],
313                                                  node[12],node[13],node[14],
314                                                  node[15],node[16],node[17] );
315       }
316       else if (nbnode == 20) {
317         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
318                                                  node[4], node[5], node[6], node[7],
319                                                  node[8], node[9], node[10],node[11],
320                                                  node[12],node[13],node[14],node[15],
321                                                  node[16],node[17],node[18],node[19],ID);
322         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
323                                                  node[4], node[5], node[6], node[7],
324                                                  node[8], node[9], node[10],node[11],
325                                                  node[12],node[13],node[14],node[15],
326                                                  node[16],node[17],node[18],node[19] );
327       }
328       else if (nbnode == 27) {
329         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
330                                                  node[4], node[5], node[6], node[7],
331                                                  node[8], node[9], node[10],node[11],
332                                                  node[12],node[13],node[14],node[15],
333                                                  node[16],node[17],node[18],node[19],
334                                                  node[20],node[21],node[22],node[23],
335                                                  node[24],node[25],node[26], ID);
336         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
337                                                  node[4], node[5], node[6], node[7],
338                                                  node[8], node[9], node[10],node[11],
339                                                  node[12],node[13],node[14],node[15],
340                                                  node[16],node[17],node[18],node[19],
341                                                  node[20],node[21],node[22],node[23],
342                                                  node[24],node[25],node[26] );
343       }
344     }
345     else if ( !features.myIsQuad )
346     {
347       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
348       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
349     }
350     else
351     {
352       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
353       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
354     }
355     break;
356
357   case SMDSAbs_Edge:
358     if ( nbnode == 2 ) {
359       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
360       else           e = mesh->AddEdge      (node[0], node[1] );
361     }
362     else if ( nbnode == 3 ) {
363       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
364       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
365     }
366     break;
367
368   case SMDSAbs_0DElement:
369     if ( nbnode == 1 ) {
370       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
371       else           e = mesh->Add0DElement      (node[0] );
372     }
373     break;
374
375   case SMDSAbs_Node:
376     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
377     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
378     break;
379
380   case SMDSAbs_Ball:
381     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
382     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
383     break;
384
385   default:;
386   }
387   if ( e ) myLastCreatedElems.push_back( e );
388   return e;
389 }
390
391 //=======================================================================
392 /*!
393  * \brief Add element
394  */
395 //=======================================================================
396
397 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
398                                                const ElemFeatures&      features)
399 {
400   vector<const SMDS_MeshNode*> nodes;
401   nodes.reserve( nodeIDs.size() );
402   vector<smIdType>::const_iterator id = nodeIDs.begin();
403   while ( id != nodeIDs.end() ) {
404     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
405       nodes.push_back( node );
406     else
407       return 0;
408   }
409   return AddElement( nodes, features );
410 }
411
412 //=======================================================================
413 //function : Remove
414 //purpose  : Remove a node or an element.
415 //           Modify a compute state of sub-meshes which become empty
416 //=======================================================================
417
418 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
419                                    const bool                   isNodes )
420 {
421   ClearLastCreated();
422
423   SMESHDS_Mesh* aMesh = GetMeshDS();
424   set< SMESH_subMesh *> smmap;
425
426   smIdType removed = 0;
427   list<smIdType>::const_iterator it = theIDs.begin();
428   for ( ; it != theIDs.end(); it++ ) {
429     const SMDS_MeshElement * elem;
430     if ( isNodes )
431       elem = aMesh->FindNode( *it );
432     else
433       elem = aMesh->FindElement( *it );
434     if ( !elem )
435       continue;
436
437     // Notify VERTEX sub-meshes about modification
438     if ( isNodes ) {
439       const SMDS_MeshNode* node = cast2Node( elem );
440       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
441         if ( int aShapeID = node->getshapeId() )
442           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
443             smmap.insert( sm );
444     }
445     // Find sub-meshes to notify about modification
446     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
447     //     while ( nodeIt->more() ) {
448     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
449     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
450     //       if ( aPosition.get() ) {
451     //         if ( int aShapeID = aPosition->GetShapeId() ) {
452     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
453     //             smmap.insert( sm );
454     //         }
455     //       }
456     //     }
457
458     // Do remove
459     if ( isNodes )
460       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
461     else
462       aMesh->RemoveElement( elem );
463     removed++;
464   }
465
466   // Notify sub-meshes about modification
467   if ( !smmap.empty() ) {
468     set< SMESH_subMesh *>::iterator smIt;
469     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
470       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
471   }
472
473   //   // Check if the whole mesh becomes empty
474   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
475   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
476
477   return removed;
478 }
479
480 //================================================================================
481 /*!
482  * \brief Remove a node and fill a hole appeared, by changing surrounding faces
483  */
484 //================================================================================
485
486 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
487 {
488   if ( ! node )
489     return;
490
491   if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
492     throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
493
494   // check that only triangles surround the node
495   for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
496   {
497     const SMDS_MeshElement* face = fIt->next();
498     if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
499       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
500     if ( face->IsQuadratic() )
501       throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
502   }
503
504   std::vector< const SMDS_MeshNode*> neighbours(2);
505   SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
506
507   bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
508
509   // if ( neighbours.size() == 2 ) // on boundary
510   // {
511   //   // check if theNode and neighbours are on a line
512   //   gp_Pnt pN = SMESH_NodeXYZ( node );
513   //   gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
514   //   gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
515   //   double dist01 = p0.Distance( p1 );
516   //   double    tol = 0.01 * dist01;
517   //   double  distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
518   //   bool   onLine = distN < tol;
519   //   toRemove = !onLine;
520   // }
521
522   if ( neighbours.empty() ) // not on boundary
523   {
524     TIDSortedElemSet linkedNodes;
525     GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
526     for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
527     if ( neighbours.empty() )
528       toRemove = true;
529   }
530
531   if ( toRemove )
532   {
533     this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
534     return;
535   }
536
537   // choose a node to replace by
538   const SMDS_MeshNode* nToReplace = nullptr;
539   SMESH_NodeXYZ           nodeXYZ = node;
540   double                  minDist = Precision::Infinite();
541   for ( const SMDS_MeshNode* n : neighbours )
542   {
543     double dist = nodeXYZ.SquareDistance( n );
544     if ( dist < minDist )
545     {
546       minDist = dist;
547       nToReplace = n;
548     }
549   }
550
551   // remove node + replace by nToReplace
552   std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
553   TListOfListOfNodes nodesToMerge( 1, nodeGroup );
554   this->MergeNodes( nodesToMerge );
555 }
556
557 //================================================================================
558 /*!
559  * \brief Create 0D elements on all nodes of the given object.
560  *  \param elements - Elements on whose nodes to create 0D elements; if empty,
561  *                    the all mesh is treated
562  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
563  *  \param duplicateElements - to add one more 0D element to a node or not
564  */
565 //================================================================================
566
567 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
568                                                    TIDSortedElemSet&       all0DElems,
569                                                    const bool              duplicateElements )
570 {
571   SMDS_ElemIteratorPtr elemIt;
572   if ( elements.empty() )
573   {
574     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
575   }
576   else
577   {
578     elemIt = SMESHUtils::elemSetIterator( elements );
579   }
580
581   while ( elemIt->more() )
582   {
583     const SMDS_MeshElement* e = elemIt->next();
584     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
585     while ( nodeIt->more() )
586     {
587       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
588       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
589       if ( duplicateElements || !it0D->more() )
590       {
591         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
592         all0DElems.insert( myLastCreatedElems.back() );
593       }
594       while ( it0D->more() )
595         all0DElems.insert( it0D->next() );
596     }
597   }
598 }
599
600 //=======================================================================
601 //function : FindShape
602 //purpose  : Return an index of the shape theElem is on
603 //           or zero if a shape not found
604 //=======================================================================
605
606 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
607 {
608   ClearLastCreated();
609
610   SMESHDS_Mesh * aMesh = GetMeshDS();
611   if ( aMesh->ShapeToMesh().IsNull() )
612     return 0;
613
614   int aShapeID = theElem->getshapeId();
615   if ( aShapeID < 1 )
616     return 0;
617
618   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
619     if ( sm->Contains( theElem ))
620       return aShapeID;
621
622   if ( theElem->GetType() == SMDSAbs_Node ) {
623     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
624   }
625   else {
626     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
627   }
628
629   TopoDS_Shape aShape; // the shape a node of theElem is on
630   if ( theElem->GetType() != SMDSAbs_Node )
631   {
632     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
633     while ( nodeIt->more() ) {
634       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
635       if ((aShapeID = node->getshapeId()) > 0) {
636         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
637           if ( sm->Contains( theElem ))
638             return aShapeID;
639           if ( aShape.IsNull() )
640             aShape = aMesh->IndexToShape( aShapeID );
641         }
642       }
643     }
644   }
645
646   // None of nodes is on a proper shape,
647   // find the shape among ancestors of aShape on which a node is
648   if ( !aShape.IsNull() ) {
649     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
650     for ( ; ancIt.More(); ancIt.Next() ) {
651       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
652       if ( sm && sm->Contains( theElem ))
653         return aMesh->ShapeToIndex( ancIt.Value() );
654     }
655   }
656   else
657   {
658     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
659     while ( const SMESHDS_SubMesh* sm = smIt->next() )
660       if ( sm->Contains( theElem ))
661         return sm->GetID();
662   }
663
664   return 0;
665 }
666
667 //=======================================================================
668 //function : IsMedium
669 //purpose  :
670 //=======================================================================
671
672 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
673                                 const SMDSAbs_ElementType typeToCheck)
674 {
675   bool isMedium = false;
676   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
677   while (it->more() && !isMedium ) {
678     const SMDS_MeshElement* elem = it->next();
679     isMedium = elem->IsMediumNode(node);
680   }
681   return isMedium;
682 }
683
684 //=======================================================================
685 //function : shiftNodesQuadTria
686 //purpose  : Shift nodes in the array corresponded to quadratic triangle
687 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
688 //=======================================================================
689
690 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
691 {
692   const SMDS_MeshNode* nd1 = aNodes[0];
693   aNodes[0] = aNodes[1];
694   aNodes[1] = aNodes[2];
695   aNodes[2] = nd1;
696   const SMDS_MeshNode* nd2 = aNodes[3];
697   aNodes[3] = aNodes[4];
698   aNodes[4] = aNodes[5];
699   aNodes[5] = nd2;
700 }
701
702 //=======================================================================
703 //function : getNodesFromTwoTria
704 //purpose  : 
705 //=======================================================================
706
707 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
708                                 const SMDS_MeshElement * theTria2,
709                                 vector< const SMDS_MeshNode*>& N1,
710                                 vector< const SMDS_MeshNode*>& N2)
711 {
712   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
713   if ( N1.size() < 6 ) return false;
714   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
715   if ( N2.size() < 6 ) return false;
716
717   int sames[3] = {-1,-1,-1};
718   int nbsames = 0;
719   int i, j;
720   for(i=0; i<3; i++) {
721     for(j=0; j<3; j++) {
722       if(N1[i]==N2[j]) {
723         sames[i] = j;
724         nbsames++;
725         break;
726       }
727     }
728   }
729   if(nbsames!=2) return false;
730   if(sames[0]>-1) {
731     shiftNodesQuadTria(N1);
732     if(sames[1]>-1) {
733       shiftNodesQuadTria(N1);
734     }
735   }
736   i = sames[0] + sames[1] + sames[2];
737   for(; i<2; i++) {
738     shiftNodesQuadTria(N2);
739   }
740   // now we receive following N1 and N2 (using numeration as in the image below)
741   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
742   // i.e. first nodes from both arrays form a new diagonal
743   return true;
744 }
745
746 //=======================================================================
747 //function : InverseDiag
748 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
749 //           but having other common link.
750 //           Return False if args are improper
751 //=======================================================================
752
753 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
754                                     const SMDS_MeshElement * theTria2 )
755 {
756   ClearLastCreated();
757
758   if ( !theTria1 || !theTria2 ||
759        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
760        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
761        theTria1->GetType() != SMDSAbs_Face ||
762        theTria2->GetType() != SMDSAbs_Face )
763     return false;
764
765   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
766       (theTria2->GetEntityType() == SMDSEntity_Triangle))
767   {
768     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
769     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
770     //    |/ |                                         | \|
771     //  B +--+ 2                                     B +--+ 2
772
773     // put nodes in array and find out indices of the same ones
774     const SMDS_MeshNode* aNodes [6];
775     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
776     int i = 0;
777     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
778     while ( it->more() ) {
779       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
780
781       if ( i > 2 ) // theTria2
782         // find same node of theTria1
783         for ( int j = 0; j < 3; j++ )
784           if ( aNodes[ i ] == aNodes[ j ]) {
785             sameInd[ j ] = i;
786             sameInd[ i ] = j;
787             break;
788           }
789       // next
790       i++;
791       if ( i == 3 ) {
792         if ( it->more() )
793           return false; // theTria1 is not a triangle
794         it = theTria2->nodesIterator();
795       }
796       if ( i == 6 && it->more() )
797         return false; // theTria2 is not a triangle
798     }
799
800     // find indices of 1,2 and of A,B in theTria1
801     int iA = -1, iB = 0, i1 = 0, i2 = 0;
802     for ( i = 0; i < 6; i++ ) {
803       if ( sameInd [ i ] == -1 ) {
804         if ( i < 3 ) i1 = i;
805         else         i2 = i;
806       }
807       else if (i < 3) {
808         if ( iA >= 0) iB = i;
809         else          iA = i;
810       }
811     }
812     // nodes 1 and 2 should not be the same
813     if ( aNodes[ i1 ] == aNodes[ i2 ] )
814       return false;
815
816     // theTria1: A->2
817     aNodes[ iA ] = aNodes[ i2 ];
818     // theTria2: B->1
819     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
820
821     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
822     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
823
824     return true;
825
826   } // end if(F1 && F2)
827
828   // check case of quadratic faces
829   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
830       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
831     return false;
832   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
833       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
834     return false;
835
836   //       5
837   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
838   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
839   //    |   / |
840   //  7 +  +  + 6
841   //    | /9  |
842   //    |/    |
843   //  4 +--+--+ 3
844   //       8
845
846   vector< const SMDS_MeshNode* > N1;
847   vector< const SMDS_MeshNode* > N2;
848   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
849     return false;
850   // now we receive following N1 and N2 (using numeration as above image)
851   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
852   // i.e. first nodes from both arrays determ new diagonal
853
854   vector< const SMDS_MeshNode*> N1new( N1.size() );
855   vector< const SMDS_MeshNode*> N2new( N2.size() );
856   N1new.back() = N1.back(); // central node of biquadratic
857   N2new.back() = N2.back();
858   N1new[0] = N1[0];  N2new[0] = N1[0];
859   N1new[1] = N2[0];  N2new[1] = N1[1];
860   N1new[2] = N2[1];  N2new[2] = N2[0];
861   N1new[3] = N1[4];  N2new[3] = N1[3];
862   N1new[4] = N2[3];  N2new[4] = N2[5];
863   N1new[5] = N1[5];  N2new[5] = N1[4];
864   // change nodes in faces
865   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
866   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
867
868   // move the central node of biquadratic triangle
869   SMESH_MesherHelper helper( *GetMesh() );
870   for ( int is2nd = 0; is2nd < 2; ++is2nd )
871   {
872     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
873     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
874     if ( nodes.size() < 7 )
875       continue;
876     helper.SetSubShape( tria->getshapeId() );
877     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
878     gp_Pnt xyz;
879     if ( F.IsNull() )
880     {
881       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
882               SMESH_NodeXYZ( nodes[4] ) +
883               SMESH_NodeXYZ( nodes[5] )) / 3.;
884     }
885     else
886     {
887       bool checkUV;
888       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
889                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
890                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
891       TopLoc_Location loc;
892       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
893       xyz = S->Value( uv.X(), uv.Y() );
894       xyz.Transform( loc );
895       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
896            nodes[6]->getshapeId() > 0 )
897         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
898     }
899     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
900   }
901   return true;
902 }
903
904 //=======================================================================
905 //function : findTriangles
906 //purpose  : find triangles sharing theNode1-theNode2 link
907 //=======================================================================
908
909 static bool findTriangles(const SMDS_MeshNode *    theNode1,
910                           const SMDS_MeshNode *    theNode2,
911                           const SMDS_MeshElement*& theTria1,
912                           const SMDS_MeshElement*& theTria2)
913 {
914   if ( !theNode1 || !theNode2 ) return false;
915
916   theTria1 = theTria2 = 0;
917
918   set< const SMDS_MeshElement* > emap;
919   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
920   while (it->more()) {
921     const SMDS_MeshElement* elem = it->next();
922     if ( elem->NbCornerNodes() == 3 )
923       emap.insert( elem );
924   }
925   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
926   while (it->more()) {
927     const SMDS_MeshElement* elem = it->next();
928     if ( emap.count( elem )) {
929       if ( !theTria1 )
930       {
931         theTria1 = elem;
932       }
933       else  
934       {
935         theTria2 = elem;
936         // theTria1 must be element with minimum ID
937         if ( theTria2->GetID() < theTria1->GetID() )
938           std::swap( theTria2, theTria1 );
939         return true;
940       }
941     }
942   }
943   return false;
944 }
945
946 //=======================================================================
947 //function : InverseDiag
948 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
949 //           with ones built on the same 4 nodes but having other common link.
950 //           Return false if proper faces not found
951 //=======================================================================
952
953 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
954                                     const SMDS_MeshNode * theNode2)
955 {
956   ClearLastCreated();
957
958   const SMDS_MeshElement *tr1, *tr2;
959   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
960     return false;
961
962   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
963        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
964     return false;
965
966   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
967       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
968
969     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
970     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
971     //    |/ |                                    | \|
972     //  B +--+ 2                                B +--+ 2
973
974     // put nodes in array
975     // and find indices of 1,2 and of A in tr1 and of B in tr2
976     int i, iA1 = 0, i1 = 0;
977     const SMDS_MeshNode* aNodes1 [3];
978     SMDS_ElemIteratorPtr it;
979     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
980       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
981       if ( aNodes1[ i ] == theNode1 )
982         iA1 = i; // node A in tr1
983       else if ( aNodes1[ i ] != theNode2 )
984         i1 = i;  // node 1
985     }
986     int iB2 = 0, i2 = 0;
987     const SMDS_MeshNode* aNodes2 [3];
988     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
989       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
990       if ( aNodes2[ i ] == theNode2 )
991         iB2 = i; // node B in tr2
992       else if ( aNodes2[ i ] != theNode1 )
993         i2 = i;  // node 2
994     }
995
996     // nodes 1 and 2 should not be the same
997     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
998       return false;
999
1000     // tr1: A->2
1001     aNodes1[ iA1 ] = aNodes2[ i2 ];
1002     // tr2: B->1
1003     aNodes2[ iB2 ] = aNodes1[ i1 ];
1004
1005     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1006     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1007
1008     return true;
1009   }
1010
1011   // check case of quadratic faces
1012   return InverseDiag(tr1,tr2);
1013 }
1014
1015 //=======================================================================
1016 //function : getQuadrangleNodes
1017 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
1018 //           fusion of triangles tr1 and tr2 having shared link on
1019 //           theNode1 and theNode2
1020 //=======================================================================
1021
1022 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
1023                         const SMDS_MeshNode *    theNode1,
1024                         const SMDS_MeshNode *    theNode2,
1025                         const SMDS_MeshElement * tr1,
1026                         const SMDS_MeshElement * tr2 )
1027 {
1028   if( tr1->NbNodes() != tr2->NbNodes() )
1029     return false;
1030
1031   // find the 4-th node to insert into tr1
1032   const SMDS_MeshNode* n4 = 0;
1033   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1034   for ( int i = 0; !n4 && i < 3; ++i )
1035   {
1036     const SMDS_MeshNode * n = cast2Node( it->next() );
1037     bool isDiag = ( n == theNode1 || n == theNode2 );
1038     if ( !isDiag )
1039       n4 = n;
1040   }
1041
1042   // Make an array of nodes to be in a quadrangle
1043   int iNode = 0, iFirstDiag = -1;
1044   it = tr1->nodesIterator();
1045   for ( int i = 0; i < 3; ++i )
1046   {
1047     const SMDS_MeshNode * n = cast2Node( it->next() );
1048     bool isDiag = ( n == theNode1 || n == theNode2 );
1049     if ( isDiag ) {
1050       if ( iFirstDiag < 0 )
1051         iFirstDiag = iNode;
1052       else if ( iNode - iFirstDiag == 1 )
1053         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1054     }
1055     else if ( n == n4 ) {
1056       return false; // tr1 and tr2 should not have all the same nodes
1057     }
1058     theQuadNodes[ iNode++ ] = n;
1059   }
1060   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1061     theQuadNodes[ iNode ] = n4;
1062
1063   return true;
1064 }
1065
1066 //=======================================================================
1067 //function : DeleteDiag
1068 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1069 //           with a quadrangle built on the same 4 nodes.
1070 //           Return false if proper faces not found
1071 //=======================================================================
1072
1073 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1074                                    const SMDS_MeshNode * theNode2)
1075 {
1076   ClearLastCreated();
1077
1078   const SMDS_MeshElement *tr1, *tr2;
1079   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1080     return false;
1081
1082   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1083        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1084     return false;
1085
1086   SMESHDS_Mesh * aMesh = GetMeshDS();
1087
1088   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1089       (tr2->GetEntityType() == SMDSEntity_Triangle))
1090   {
1091     const SMDS_MeshNode* aNodes [ 4 ];
1092     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1093       return false;
1094
1095     const SMDS_MeshElement* newElem = 0;
1096     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1097     myLastCreatedElems.push_back(newElem);
1098     AddToSameGroups( newElem, tr1, aMesh );
1099     int aShapeId = tr1->getshapeId();
1100     if ( aShapeId )
1101       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1102
1103     aMesh->RemoveElement( tr1 );
1104     aMesh->RemoveElement( tr2 );
1105
1106     return true;
1107   }
1108
1109   // check case of quadratic faces
1110   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1111     return false;
1112   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1113     return false;
1114
1115   //       5
1116   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1117   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1118   //    |   / |
1119   //  7 +  +  + 6
1120   //    | /9  |
1121   //    |/    |
1122   //  4 +--+--+ 3
1123   //       8
1124
1125   vector< const SMDS_MeshNode* > N1;
1126   vector< const SMDS_MeshNode* > N2;
1127   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1128     return false;
1129   // now we receive following N1 and N2 (using numeration as above image)
1130   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1131   // i.e. first nodes from both arrays determ new diagonal
1132
1133   const SMDS_MeshNode* aNodes[8];
1134   aNodes[0] = N1[0];
1135   aNodes[1] = N1[1];
1136   aNodes[2] = N2[0];
1137   aNodes[3] = N2[1];
1138   aNodes[4] = N1[3];
1139   aNodes[5] = N2[5];
1140   aNodes[6] = N2[3];
1141   aNodes[7] = N1[5];
1142
1143   const SMDS_MeshElement* newElem = 0;
1144   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1145                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1146   myLastCreatedElems.push_back(newElem);
1147   AddToSameGroups( newElem, tr1, aMesh );
1148   int aShapeId = tr1->getshapeId();
1149   if ( aShapeId )
1150   {
1151     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1152   }
1153   aMesh->RemoveElement( tr1 );
1154   aMesh->RemoveElement( tr2 );
1155
1156   // remove middle node (9)
1157   GetMeshDS()->RemoveNode( N1[4] );
1158
1159   return true;
1160 }
1161
1162 //=======================================================================
1163 //function : SplitEdge
1164 //purpose  : Replace each triangle bound by theNode1-theNode2 segment with
1165 //           two triangles by connecting a node made on the link with a node opposite to the link.
1166 //=======================================================================
1167
1168 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1169                                   const SMDS_MeshNode * theNode2,
1170                                   double                thePosition)
1171 {
1172   ClearLastCreated();
1173
1174   SMESHDS_Mesh * mesh = GetMeshDS();
1175
1176   // Get triangles and segments to divide
1177
1178   std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1179   std::vector<const SMDS_MeshElement *> foundElems;
1180   if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1181     throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1182                             << theNode1->GetID() << " - " << theNode2->GetID());
1183
1184   SMESH_MesherHelper helper( *GetMesh() );
1185
1186   for ( const SMDS_MeshElement * elem : foundElems )
1187   {
1188     SMDSAbs_ElementType type = elem->GetType();
1189     switch ( type ) {
1190     case SMDSAbs_Volume:
1191       throw SALOME_Exception( "Can't split an edge of a volume");
1192       break;
1193
1194     case SMDSAbs_Face:
1195       if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1196         throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1197       if ( elem->IsQuadratic() )
1198       {
1199         helper.SetIsQuadratic( true );
1200         helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1201         helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1202       }
1203       break;
1204
1205     case SMDSAbs_Edge:
1206       if ( elem->IsQuadratic() )
1207       {
1208         helper.SetIsQuadratic( true );
1209         helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1210       }
1211       break;
1212     default:;
1213     }
1214   }
1215
1216   // Make a new node
1217
1218   const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1219
1220   gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1221                         SMESH_NodeXYZ( theNode2 ) * thePosition );
1222
1223   const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1224   if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1225   {
1226     Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1227     double  tol = 100 * helper.MaxTolerance( S );
1228     gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1229     if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1230     {
1231       newNodeXYZ = surface->Value( uv );
1232       if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1233         nPos->SetParameters( uv.X(), uv.Y() );
1234     }
1235   }
1236   if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1237   {
1238     mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1239     double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1240     helper.ToFixNodeParameters( true );
1241     if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1242       newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1243   }
1244   mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1245
1246   // Split triangles and segments
1247
1248   std::vector<const SMDS_MeshNode *> nodes( 7 );
1249   for ( const SMDS_MeshElement * elem : foundElems )
1250   {
1251     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1252     nodes.resize( elem->NbCornerNodes() + 1 );
1253     nodes.back() = nodes[0];
1254
1255     smIdType id = elem->GetID();
1256     int shapeID = elem->GetShapeID();
1257
1258     const SMDS_MeshNode* centralNode = nullptr;
1259     if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1260       centralNode = elem->GetNode( 6 );
1261
1262     mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1263     if ( centralNode )
1264       mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1265
1266     for ( size_t i = 1; i < nodes.size(); ++i )
1267     {
1268       const SMDS_MeshNode* n1 = nodes[i-1];
1269       const SMDS_MeshNode* n2 = nodes[i];
1270       const SMDS_MeshElement* newElem;
1271       if ( nodes.size() == 4 ) //    triangle
1272       {
1273         bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1274         bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1275         if ( isDiag1 && isDiag2 )
1276           continue;
1277
1278         newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1279       }
1280       else //    segment
1281       {
1282         newElem = helper.AddEdge( n1, nodeOnLink, id );
1283       }
1284       myLastCreatedElems.push_back( newElem );
1285       AddToSameGroups( newElem, elem, mesh );
1286       if ( shapeID )
1287         mesh->SetMeshElementOnShape( newElem, shapeID );
1288       id = 0;
1289     }
1290   }
1291   return;
1292 }
1293
1294 //=======================================================================
1295 //function : SplitFace
1296 //purpose  : Split a face into triangles each formed by two nodes of the 
1297 //           face and a new node added at the given coordinates.
1298 //=======================================================================
1299
1300 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1301                                   double                   theX,
1302                                   double                   theY,
1303                                   double                   theZ )
1304 {
1305   ClearLastCreated();
1306
1307   if ( !theFace )
1308     throw SALOME_Exception("Null face given");
1309   if ( theFace->GetType() != SMDSAbs_Face )
1310     throw SALOME_Exception("Not a face given");
1311
1312   SMESHDS_Mesh * mesh = GetMeshDS();
1313
1314   SMESH_MesherHelper helper( *GetMesh() );
1315   if ( theFace->IsQuadratic() )
1316   {
1317     helper.SetIsQuadratic( true );
1318     helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1319   }
1320   const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1321   helper.SetSubShape( shape );
1322   helper.SetElementsOnShape( true );
1323
1324   // Make a new node
1325
1326   const SMDS_MeshNode* centralNode = nullptr;
1327   if (      theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1328     centralNode = theFace->GetNode( 6 );
1329   else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1330     centralNode = theFace->GetNode( 8 );
1331
1332   if ( centralNode )
1333   {
1334     helper.SetIsBiQuadratic( true );
1335     mesh->MoveNode( centralNode, theX, theY, theZ );
1336   }
1337   else
1338     centralNode = helper.AddNode( theX, theY, theZ );
1339
1340
1341   // Split theFace
1342
1343   std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1344   nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1345   nodes.resize( theFace->NbCornerNodes() + 1 );
1346   nodes.back() = nodes[0];
1347
1348   smIdType id = theFace->GetID();
1349   int shapeID = theFace->GetShapeID();
1350
1351   mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1352
1353   for ( size_t i = 1; i < nodes.size(); ++i )
1354   {
1355     const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1356
1357     myLastCreatedElems.push_back( newElem );
1358     AddToSameGroups( newElem, theFace, mesh );
1359     if ( shapeID )
1360       mesh->SetMeshElementOnShape( newElem, shapeID );
1361     id = 0;
1362   }
1363   return;
1364 }
1365
1366 //=======================================================================
1367 //function : Reorient
1368 //purpose  : Reverse theElement orientation
1369 //=======================================================================
1370
1371 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1372 {
1373   ClearLastCreated();
1374
1375   if (!theElem)
1376     return false;
1377   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1378   if ( !it || !it->more() )
1379     return false;
1380
1381   const SMDSAbs_ElementType type = theElem->GetType();
1382   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1383     return false;
1384
1385   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1386   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1387   {
1388     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1389     if (!aPolyedre) {
1390       MESSAGE("Warning: bad volumic element");
1391       return false;
1392     }
1393     SMDS_VolumeTool vTool( aPolyedre );
1394     const int nbFaces = vTool.NbFaces();
1395     vector<int> quantities( nbFaces );
1396     vector<const SMDS_MeshNode *> poly_nodes;
1397
1398     // check if all facets are oriented equally
1399     bool sameOri = true;
1400     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1401     for (int iface = 0; iface < nbFaces; iface++)
1402     {
1403       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1404       if ( facetOri[ iface ] != facetOri[ 0 ])
1405         sameOri = false;
1406     }
1407
1408     // reverse faces of the polyhedron
1409     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1410     poly_nodes.reserve( vTool.NbNodes() );
1411     for ( int iface = 0; iface < nbFaces; iface++ )
1412     {
1413       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1414       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1415       bool toReverse = ( facetOri[ iface ] != neededOri );
1416
1417       quantities[ iface ] = nbFaceNodes;
1418
1419       if ( toReverse )
1420         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1421           poly_nodes.push_back( nodes[ inode ]);
1422       else
1423         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1424     }
1425     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1426   }
1427   else // other elements
1428   {
1429     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1430     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1431     if ( interlace.empty() )
1432     {
1433       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1434     }
1435     else
1436     {
1437       SMDS_MeshCell::applyInterlace( interlace, nodes );
1438     }
1439     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1440   }
1441   return false;
1442 }
1443
1444 //================================================================================
1445 /*!
1446  * \brief Reorient faces.
1447  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1448  * \param theDirection - desired direction of normal of \a theRefFaces.
1449  *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1450  * \param theRefFaces - correctly oriented faces whose orientation defines
1451  *        orientation of other faces.
1452  * \return number of reoriented faces.
1453  */
1454 //================================================================================
1455
1456 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
1457                                   const gp_Vec&       theDirection,
1458                                   TIDSortedElemSet &  theRefFaces,
1459                                   bool                theAllowNonManifold )
1460 {
1461   int nbReori = 0;
1462
1463   if ( theFaces.empty() )
1464   {
1465     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1466     while ( fIt->more() )
1467       theFaces.insert( theFaces.end(), fIt->next() );
1468
1469     if ( theFaces.empty() )
1470       return nbReori;
1471   }
1472
1473   // orient theRefFaces according to theDirection
1474   if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1475     for ( const SMDS_MeshElement* refFace : theRefFaces )
1476     {
1477       gp_XYZ normal;
1478       SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1479       if ( normal * theDirection.XYZ() < 0 )
1480         nbReori += Reorient( refFace );
1481     }
1482
1483   // mark reference faces
1484   GetMeshDS()->SetAllCellsNotMarked();
1485   for ( const SMDS_MeshElement* refFace : theRefFaces )
1486     refFace->setIsMarked( true );
1487
1488   // erase reference faces from theFaces
1489   for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1490     if ( (*fIt)->isMarked() )
1491       fIt = theFaces.erase( fIt );
1492     else
1493       ++fIt;
1494
1495   if ( theRefFaces.empty() )
1496   {
1497     theRefFaces.insert( *theFaces.begin() );
1498     theFaces.erase( theFaces.begin() );
1499   }
1500
1501   // Orient theFaces
1502
1503   // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1504   //   theFaces.erase( theFace );
1505
1506   int nodeInd1, nodeInd2;
1507   const SMDS_MeshElement*           refFace, *otherFace;
1508   vector< const SMDS_MeshElement* > facesNearLink;
1509   vector< std::pair< int, int > >   nodeIndsOfFace;
1510   TIDSortedElemSet                  avoidSet, emptySet;
1511   NCollection_Map< SMESH_TLink, SMESH_TLinkHasher > checkedLinks;
1512
1513   while ( !theRefFaces.empty() )
1514   {
1515     auto refFaceIt = theRefFaces.begin();
1516     refFace = *refFaceIt;
1517     theRefFaces.erase( refFaceIt );
1518
1519     avoidSet.clear();
1520     avoidSet.insert( refFace );
1521
1522     NLink link( refFace->GetNode( 0 ), nullptr );
1523
1524     const int nbNodes = refFace->NbCornerNodes();
1525     for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1526     {
1527       link.second = refFace->GetNode(( i+1 ) % nbNodes );
1528       bool isLinkVisited = checkedLinks.Contains( link );
1529       if ( isLinkVisited )
1530       {
1531         // link has already been checked and won't be encountered more
1532         // if the group (theFaces) is manifold
1533         //checkedLinks.erase( linkIt_isNew.first );
1534       }
1535       else
1536       {
1537         checkedLinks.Add( link );
1538
1539         facesNearLink.clear();
1540         nodeIndsOfFace.clear();
1541         TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1542
1543         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1544                                                              emptySet, avoidSet,
1545                                                              &nodeInd1, &nodeInd2 )))
1546         {
1547           if (( otherFace->isMarked() ) || // ref face
1548               (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1549           {
1550             facesNearLink.push_back( otherFace );
1551             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1552           }
1553           avoidSet.insert( otherFace );
1554         }
1555         if ( facesNearLink.size() > 1 )
1556         {
1557           // NON-MANIFOLD mesh shell !
1558           if ( !theAllowNonManifold )
1559           {
1560             throw SALOME_Exception("Non-manifold topology of groups");
1561           }
1562           // select a face most co-directed with refFace,
1563           // other faces won't be visited this time
1564           gp_XYZ NF, NOF;
1565           SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1566           double proj, maxProj = -1;
1567           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1568           {
1569             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1570             if (( proj = Abs( NF * NOF )) > maxProj )
1571             {
1572               maxProj = proj;
1573               otherFace = facesNearLink[i];
1574               nodeInd1  = nodeIndsOfFace[i].first;
1575               nodeInd2  = nodeIndsOfFace[i].second;
1576             }
1577           }
1578           // not to visit rejected faces
1579           // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1580           //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1581           //     visitedFaces.insert( facesNearLink[i] );
1582         }
1583         else if ( facesNearLink.size() == 1 )
1584         {
1585           otherFace = facesNearLink[0];
1586           nodeInd1  = nodeIndsOfFace.back().first;
1587           nodeInd2  = nodeIndsOfFace.back().second;
1588         }
1589         if ( otherFace )
1590         {
1591           // link must be reverse in otherFace if orientation of otherFace
1592           // is same as that of refFace
1593           if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1594           {
1595             if ( otherFace->isMarked() )
1596               throw SALOME_Exception("Different orientation of reference faces");
1597             nbReori += Reorient( otherFace );
1598           }
1599           if ( !otherFace->isMarked() )
1600           {
1601             theRefFaces.insert( otherFace );
1602             if ( objFaceIt != theFaces.end() )
1603               theFaces.erase( objFaceIt );
1604           }
1605         }
1606       }
1607       link.first = link.second; // reverse the link
1608
1609     } // loop on links of refFace
1610
1611     if ( theRefFaces.empty() && !theFaces.empty() )
1612     {
1613       theRefFaces.insert( *theFaces.begin() );
1614       theFaces.erase( theFaces.begin() );
1615     }
1616
1617   } // while ( !theRefFaces.empty() )
1618
1619   return nbReori;
1620 }
1621
1622 //================================================================================
1623 /*!
1624  * \brief Reorient faces basing on orientation of adjacent volumes.
1625  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1626  * \param theVolumes - reference volumes.
1627  * \param theOutsideNormal - to orient faces to have their normal
1628  *        pointing either \a outside or \a inside the adjacent volumes.
1629  * \return number of reoriented faces.
1630  */
1631 //================================================================================
1632
1633 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1634                                       TIDSortedElemSet & theVolumes,
1635                                       const bool         theOutsideNormal)
1636 {
1637   int nbReori = 0;
1638
1639   SMDS_ElemIteratorPtr faceIt;
1640   if ( theFaces.empty() )
1641     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1642   else
1643     faceIt = SMESHUtils::elemSetIterator( theFaces );
1644
1645   vector< const SMDS_MeshNode* > faceNodes;
1646   TIDSortedElemSet checkedVolumes;
1647   set< const SMDS_MeshNode* > faceNodesSet;
1648   SMDS_VolumeTool volumeTool;
1649
1650   while ( faceIt->more() ) // loop on given faces
1651   {
1652     const SMDS_MeshElement* face = faceIt->next();
1653     if ( face->GetType() != SMDSAbs_Face )
1654       continue;
1655
1656     const size_t nbCornersNodes = face->NbCornerNodes();
1657     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1658
1659     checkedVolumes.clear();
1660     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1661     while ( vIt->more() )
1662     {
1663       const SMDS_MeshElement* volume = vIt->next();
1664
1665       if ( !checkedVolumes.insert( volume ).second )
1666         continue;
1667       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1668         continue;
1669
1670       // is volume adjacent?
1671       bool allNodesCommon = true;
1672       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1673         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1674       if ( !allNodesCommon )
1675         continue;
1676
1677       // get nodes of a corresponding volume facet
1678       faceNodesSet.clear();
1679       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1680       volumeTool.Set( volume );
1681       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1682       if ( facetID < 0 ) continue;
1683       volumeTool.SetExternalNormal();
1684       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1685
1686       // compare order of faceNodes and facetNodes
1687       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1688       int iNN[2];
1689       for ( int i = 0; i < 2; ++i )
1690       {
1691         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1692         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1693           if ( faceNodes[ iN ] == n )
1694           {
1695             iNN[ i ] = iN;
1696             break;
1697           }
1698       }
1699       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1700       if ( isOutside != theOutsideNormal )
1701         nbReori += Reorient( face );
1702     }
1703   }  // loop on given faces
1704
1705   return nbReori;
1706 }
1707
1708 //=======================================================================
1709 //function : getBadRate
1710 //purpose  :
1711 //=======================================================================
1712
1713 static double getBadRate (const SMDS_MeshElement*               theElem,
1714                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1715 {
1716   SMESH::Controls::TSequenceOfXYZ P;
1717   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1718     return 1e100;
1719   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1720   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1721 }
1722
1723 //=======================================================================
1724 //function : QuadToTri
1725 //purpose  : Cut quadrangles into triangles.
1726 //           theCrit is used to select a diagonal to cut
1727 //=======================================================================
1728
1729 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1730                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1731 {
1732   ClearLastCreated();
1733
1734   if ( !theCrit.get() )
1735     return false;
1736
1737   SMESHDS_Mesh *       aMesh = GetMeshDS();
1738   Handle(Geom_Surface) surface;
1739   SMESH_MesherHelper   helper( *GetMesh() );
1740
1741   myLastCreatedElems.reserve( theElems.size() * 2 );
1742
1743   TIDSortedElemSet::iterator itElem;
1744   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1745   {
1746     const SMDS_MeshElement* elem = *itElem;
1747     if ( !elem || elem->GetType() != SMDSAbs_Face )
1748       continue;
1749     if ( elem->NbCornerNodes() != 4 )
1750       continue;
1751
1752     // retrieve element nodes
1753     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1754
1755     // compare two sets of possible triangles
1756     double aBadRate1, aBadRate2; // to what extent a set is bad
1757     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1758     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1759     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1760
1761     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1762     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1763     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1764
1765     const int aShapeId = FindShape( elem );
1766     const SMDS_MeshElement* newElem1 = 0;
1767     const SMDS_MeshElement* newElem2 = 0;
1768
1769     if ( !elem->IsQuadratic() ) // split linear quadrangle
1770     {
1771       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1772       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1773       if ( aBadRate1 <= aBadRate2 ) {
1774         // tr1 + tr2 is better
1775         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1776         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1777       }
1778       else {
1779         // tr3 + tr4 is better
1780         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1781         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1782       }
1783     }
1784     else // split quadratic quadrangle
1785     {
1786       helper.SetIsQuadratic( true );
1787       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1788
1789       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1790       if ( aNodes.size() == 9 )
1791       {
1792         helper.SetIsBiQuadratic( true );
1793         if ( aBadRate1 <= aBadRate2 )
1794           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1795         else
1796           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1797       }
1798       // create a new element
1799       if ( aBadRate1 <= aBadRate2 ) {
1800         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1801         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1802       }
1803       else {
1804         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1805         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1806       }
1807     } // quadratic case
1808
1809     // care of a new element
1810
1811     myLastCreatedElems.push_back(newElem1);
1812     myLastCreatedElems.push_back(newElem2);
1813     AddToSameGroups( newElem1, elem, aMesh );
1814     AddToSameGroups( newElem2, elem, aMesh );
1815
1816     // put a new triangle on the same shape
1817     if ( aShapeId )
1818       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1819     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1820
1821     aMesh->RemoveElement( elem );
1822   }
1823   return true;
1824 }
1825
1826 //=======================================================================
1827 /*!
1828  * \brief Split each of given quadrangles into 4 triangles.
1829  * \param theElems - The faces to be split. If empty all faces are split.
1830  */
1831 //=======================================================================
1832
1833 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1834 {
1835   ClearLastCreated();
1836   myLastCreatedElems.reserve( theElems.size() * 4 );
1837
1838   SMESH_MesherHelper helper( *GetMesh() );
1839   helper.SetElementsOnShape( true );
1840
1841   // get standalone groups of faces
1842   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1843   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1844     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1845       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1846         allFaceGroups.push_back( & group->SMDSGroup() );
1847
1848   bool   checkUV;
1849   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1850   gp_XYZ xyz[9];
1851   vector< const SMDS_MeshNode* > nodes;
1852   SMESHDS_SubMesh*               subMeshDS = 0;
1853   TopoDS_Face                    F;
1854   Handle(Geom_Surface)           surface;
1855   TopLoc_Location                loc;
1856
1857   SMDS_ElemIteratorPtr faceIt;
1858   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1859   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1860
1861   while ( faceIt->more() )
1862   {
1863     const SMDS_MeshElement* quad = faceIt->next();
1864     if ( !quad || quad->NbCornerNodes() != 4 )
1865       continue;
1866
1867     // get a surface the quad is on
1868
1869     if ( quad->getshapeId() < 1 )
1870     {
1871       F.Nullify();
1872       helper.SetSubShape( 0 );
1873       subMeshDS = 0;
1874     }
1875     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1876     {
1877       helper.SetSubShape( quad->getshapeId() );
1878       if ( !helper.GetSubShape().IsNull() &&
1879            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1880       {
1881         F = TopoDS::Face( helper.GetSubShape() );
1882         surface = BRep_Tool::Surface( F, loc );
1883         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1884       }
1885       else
1886       {
1887         helper.SetSubShape( 0 );
1888         subMeshDS = 0;
1889       }
1890     }
1891
1892     // create a central node
1893
1894     const SMDS_MeshNode* nCentral;
1895     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1896
1897     if ( nodes.size() == 9 )
1898     {
1899       nCentral = nodes.back();
1900     }
1901     else
1902     {
1903       size_t iN = 0;
1904       if ( F.IsNull() )
1905       {
1906         for ( ; iN < nodes.size(); ++iN )
1907           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1908
1909         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1910           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1911
1912         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1913                                    xyz[0], xyz[1], xyz[2], xyz[3],
1914                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1915       }
1916       else
1917       {
1918         for ( ; iN < nodes.size(); ++iN )
1919           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1920
1921         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1922           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1923
1924         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1925                                   uv[0], uv[1], uv[2], uv[3],
1926                                   uv[4], uv[5], uv[6], uv[7] );
1927
1928         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1929         xyz[ 8 ] = p.XYZ();
1930       }
1931
1932       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1933                                  uv[8].X(), uv[8].Y() );
1934       myLastCreatedNodes.push_back( nCentral );
1935     }
1936
1937     helper.SetIsQuadratic  ( nodes.size() > 4 );
1938     helper.SetIsBiQuadratic( nodes.size() == 9 );
1939     if ( helper.GetIsQuadratic() )
1940       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1941
1942     // select groups to update
1943     faceGroups.clear();
1944     for ( SMDS_MeshGroup* group : allFaceGroups )
1945       if ( group->Remove( quad ))
1946         faceGroups.push_back( group );
1947
1948     // create 4 triangles
1949
1950     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1951
1952     for ( int i = 0; i < 4; ++i )
1953     {
1954       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1955                                                nodes[(i+1)%4],
1956                                                nCentral );
1957       myLastCreatedElems.push_back( tria );
1958       for ( SMDS_MeshGroup* group : faceGroups )
1959         group->Add( tria );
1960     }
1961   }
1962 }
1963
1964 //=======================================================================
1965 //function : BestSplit
1966 //purpose  : Find better diagonal for cutting.
1967 //=======================================================================
1968
1969 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1970                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1971 {
1972   ClearLastCreated();
1973
1974   if (!theCrit.get())
1975     return -1;
1976
1977   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1978     return -1;
1979
1980   if( theQuad->NbNodes()==4 ||
1981       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1982
1983     // retrieve element nodes
1984     const SMDS_MeshNode* aNodes [4];
1985     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1986     int i = 0;
1987     //while (itN->more())
1988     while (i<4) {
1989       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1990     }
1991     // compare two sets of possible triangles
1992     double aBadRate1, aBadRate2; // to what extent a set is bad
1993     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1994     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1995     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1996
1997     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1998     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1999     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
2000     // for MaxElementLength2D functor we return minimum diagonal for splitting,
2001     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
2002     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2003       return 1; // diagonal 1-3
2004
2005     return 2; // diagonal 2-4
2006   }
2007   return -1;
2008 }
2009
2010 namespace
2011 {
2012   // Methods of splitting volumes into tetra
2013
2014   const int theHexTo5_1[5*4+1] =
2015     {
2016       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
2017     };
2018   const int theHexTo5_2[5*4+1] =
2019     {
2020       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
2021     };
2022   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2023
2024   const int theHexTo6_1[6*4+1] =
2025     {
2026       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
2027     };
2028   const int theHexTo6_2[6*4+1] =
2029     {
2030       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
2031     };
2032   const int theHexTo6_3[6*4+1] =
2033     {
2034       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
2035     };
2036   const int theHexTo6_4[6*4+1] =
2037     {
2038       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
2039     };
2040   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2041
2042   const int thePyraTo2_1[2*4+1] =
2043     {
2044       0, 1, 2, 4,    0, 2, 3, 4,   -1
2045     };
2046   const int thePyraTo2_2[2*4+1] =
2047     {
2048       1, 2, 3, 4,    1, 3, 0, 4,   -1
2049     };
2050   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2051
2052   const int thePentaTo3_1[3*4+1] =
2053     {
2054       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
2055     };
2056   const int thePentaTo3_2[3*4+1] =
2057     {
2058       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
2059     };
2060   const int thePentaTo3_3[3*4+1] =
2061     {
2062       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
2063     };
2064   const int thePentaTo3_4[3*4+1] =
2065     {
2066       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
2067     };
2068   const int thePentaTo3_5[3*4+1] =
2069     {
2070       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
2071     };
2072   const int thePentaTo3_6[3*4+1] =
2073     {
2074       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
2075     };
2076   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2077                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2078
2079   // Methods of splitting hexahedron into prisms
2080
2081   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2082     {
2083       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
2084     };
2085   const int theHexTo4Prisms_LR[6*4+1] = // left-right
2086     {
2087       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
2088     };
2089   const int theHexTo4Prisms_FB[6*4+1] = // front-back
2090     {
2091       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
2092     };
2093
2094   const int theHexTo2Prisms_BT_1[6*2+1] =
2095     {
2096       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
2097     };
2098   const int theHexTo2Prisms_BT_2[6*2+1] =
2099     {
2100       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
2101     };
2102   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2103
2104   const int theHexTo2Prisms_LR_1[6*2+1] =
2105     {
2106       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2107     };
2108   const int theHexTo2Prisms_LR_2[6*2+1] =
2109     {
2110       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
2111     };
2112   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2113
2114   const int theHexTo2Prisms_FB_1[6*2+1] =
2115     {
2116       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
2117     };
2118   const int theHexTo2Prisms_FB_2[6*2+1] =
2119     {
2120       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
2121     };
2122   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2123
2124
2125   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2126   {
2127     int _n1, _n2, _n3;
2128     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2129     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2130     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
2131                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2132   };
2133   struct TSplitMethod
2134   {
2135     int        _nbSplits;
2136     int        _nbCorners;
2137     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2138     bool       _baryNode;     //!< additional node is to be created at cell barycenter
2139     bool       _ownConn;      //!< to delete _connectivity in destructor
2140     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2141
2142     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2143       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2144     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2145     TSplitMethod(const TSplitMethod &splitMethod)
2146       : _nbSplits(splitMethod._nbSplits),
2147         _nbCorners(splitMethod._nbCorners),
2148         _baryNode(splitMethod._baryNode),
2149         _ownConn(splitMethod._ownConn),
2150         _faceBaryNode(splitMethod._faceBaryNode)
2151     {
2152       _connectivity = splitMethod._connectivity;
2153       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2154       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2155     }
2156     bool hasFacet( const TTriangleFacet& facet ) const
2157     {
2158       if ( _nbCorners == 4 )
2159       {
2160         const int* tetConn = _connectivity;
2161         for ( ; tetConn[0] >= 0; tetConn += 4 )
2162           if (( facet.contains( tetConn[0] ) +
2163                 facet.contains( tetConn[1] ) +
2164                 facet.contains( tetConn[2] ) +
2165                 facet.contains( tetConn[3] )) == 3 )
2166             return true;
2167       }
2168       else // prism, _nbCorners == 6
2169       {
2170         const int* prismConn = _connectivity;
2171         for ( ; prismConn[0] >= 0; prismConn += 6 )
2172         {
2173           if (( facet.contains( prismConn[0] ) &&
2174                 facet.contains( prismConn[1] ) &&
2175                 facet.contains( prismConn[2] ))
2176               ||
2177               ( facet.contains( prismConn[3] ) &&
2178                 facet.contains( prismConn[4] ) &&
2179                 facet.contains( prismConn[5] )))
2180             return true;
2181         }
2182       }
2183       return false;
2184     }
2185   };
2186
2187   //=======================================================================
2188   /*!
2189    * \brief return TSplitMethod for the given element to split into tetrahedra
2190    */
2191   //=======================================================================
2192
2193   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2194   {
2195     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2196
2197     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2198     // an edge and a face barycenter; tertaherdons are based on triangles and
2199     // a volume barycenter
2200     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2201
2202     // Find out how adjacent volumes are split
2203
2204     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2205     int hasAdjacentSplits = 0, maxTetConnSize = 0;
2206     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2207     {
2208       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2209       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2210       if ( nbNodes < 4 ) continue;
2211
2212       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2213       const int* nInd = vol.GetFaceNodesIndices( iF );
2214       if ( nbNodes == 4 )
2215       {
2216         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2217         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2218         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2219         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2220       }
2221       else
2222       {
2223         int iCom = 0; // common node of triangle faces to split into
2224         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2225         {
2226           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
2227                                nInd[ iQ * ( (iCom+1)%nbNodes )],
2228                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
2229           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
2230                                nInd[ iQ * ( (iCom+2)%nbNodes )],
2231                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
2232           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2233           {
2234             triaSplits.push_back( t012 );
2235             triaSplits.push_back( t023 );
2236             break;
2237           }
2238         }
2239       }
2240       if ( !triaSplits.empty() )
2241         hasAdjacentSplits = true;
2242     }
2243
2244     // Among variants of split method select one compliant with adjacent volumes
2245
2246     TSplitMethod method;
2247     if ( !vol.Element()->IsPoly() && !is24TetMode )
2248     {
2249       int nbVariants = 2, nbTet = 0;
2250       const int** connVariants = 0;
2251       switch ( vol.Element()->GetEntityType() )
2252       {
2253       case SMDSEntity_Hexa:
2254       case SMDSEntity_Quad_Hexa:
2255       case SMDSEntity_TriQuad_Hexa:
2256         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2257           connVariants = theHexTo5, nbTet = 5;
2258         else
2259           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2260         break;
2261       case SMDSEntity_Pyramid:
2262       case SMDSEntity_Quad_Pyramid:
2263         connVariants = thePyraTo2;  nbTet = 2;
2264         break;
2265       case SMDSEntity_Penta:
2266       case SMDSEntity_Quad_Penta:
2267       case SMDSEntity_BiQuad_Penta:
2268         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2269         break;
2270       default:
2271         nbVariants = 0;
2272       }
2273       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2274       {
2275         // check method compliance with adjacent tetras,
2276         // all found splits must be among facets of tetras described by this method
2277         method = TSplitMethod( nbTet, connVariants[variant] );
2278         if ( hasAdjacentSplits && method._nbSplits > 0 )
2279         {
2280           bool facetCreated = true;
2281           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2282           {
2283             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2284             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2285               facetCreated = method.hasFacet( *facet );
2286           }
2287           if ( !facetCreated )
2288             method = TSplitMethod(0); // incompatible method
2289         }
2290       }
2291     }
2292     if ( method._nbSplits < 1 )
2293     {
2294       // No standard method is applicable, use a generic solution:
2295       // each facet of a volume is split into triangles and
2296       // each of triangles and a volume barycenter form a tetrahedron.
2297
2298       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2299
2300       int* connectivity = new int[ maxTetConnSize + 1 ];
2301       method._connectivity = connectivity;
2302       method._ownConn = true;
2303       method._baryNode = !isHex27; // to create central node or not
2304
2305       int connSize = 0;
2306       int baryCenInd = vol.NbNodes() - int( isHex27 );
2307       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2308       {
2309         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2310         const int*   nInd = vol.GetFaceNodesIndices( iF );
2311         // find common node of triangle facets of tetra to create
2312         int iCommon = 0; // index in linear numeration
2313         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2314         if ( !triaSplits.empty() )
2315         {
2316           // by found facets
2317           const TTriangleFacet* facet = &triaSplits.front();
2318           for ( ; iCommon < nbNodes-1 ; ++iCommon )
2319             if ( facet->contains( nInd[ iQ * iCommon ]) &&
2320                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2321               break;
2322         }
2323         else if ( nbNodes > 3 && !is24TetMode )
2324         {
2325           // find the best method of splitting into triangles by aspect ratio
2326           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2327           map< double, int > badness2iCommon;
2328           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2329           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2330           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2331           {
2332             double badness = 0;
2333             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2334             {
2335               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2336                                       nodes[ iQ*((iLast-1)%nbNodes)],
2337                                       nodes[ iQ*((iLast  )%nbNodes)]);
2338               badness += getBadRate( &tria, aspectRatio );
2339             }
2340             badness2iCommon.insert( make_pair( badness, iCommon ));
2341           }
2342           // use iCommon with lowest badness
2343           iCommon = badness2iCommon.begin()->second;
2344         }
2345         if ( iCommon >= nbNodes )
2346           iCommon = 0; // something wrong
2347
2348         // fill connectivity of tetrahedra based on a current face
2349         int nbTet = nbNodes - 2;
2350         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2351         {
2352           int faceBaryCenInd;
2353           if ( isHex27 )
2354           {
2355             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2356             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2357           }
2358           else
2359           {
2360             method._faceBaryNode[ iF ] = 0;
2361             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2362           }
2363           nbTet = nbNodes;
2364           for ( int i = 0; i < nbTet; ++i )
2365           {
2366             int i1 = i, i2 = (i+1) % nbNodes;
2367             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2368             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2369             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2370             connectivity[ connSize++ ] = faceBaryCenInd;
2371             connectivity[ connSize++ ] = baryCenInd;
2372           }
2373         }
2374         else
2375         {
2376           for ( int i = 0; i < nbTet; ++i )
2377           {
2378             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2379             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2380             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2381             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2382             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2383             connectivity[ connSize++ ] = baryCenInd;
2384           }
2385         }
2386         method._nbSplits += nbTet;
2387
2388       } // loop on volume faces
2389
2390       connectivity[ connSize++ ] = -1;
2391
2392     } // end of generic solution
2393
2394     return method;
2395   }
2396   //=======================================================================
2397   /*!
2398    * \brief return TSplitMethod to split haxhedron into prisms
2399    */
2400   //=======================================================================
2401
2402   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2403                                     const int        methodFlags,
2404                                     const int        facetToSplit)
2405   {
2406     TSplitMethod method;
2407
2408     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2409     // B, T, L, B, R, F
2410     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2411
2412     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2413     {
2414       static TSplitMethod to4methods[4]; // order BT, LR, FB
2415       if ( to4methods[iF]._nbSplits == 0 )
2416       {
2417         switch ( iF ) {
2418         case 0:
2419           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2420           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2421           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2422           break;
2423         case 1:
2424           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2425           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2426           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2427           break;
2428         case 2:
2429           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2430           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2431           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2432           break;
2433         default: return to4methods[3];
2434         }
2435         to4methods[iF]._nbSplits  = 4;
2436         to4methods[iF]._nbCorners = 6;
2437       }
2438       method = to4methods[iF];
2439       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2440       return method;
2441     }
2442     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2443
2444     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2445
2446     const int nbVariants = 2, nbSplits = 2;
2447     const int** connVariants = 0;
2448     switch ( iF ) {
2449     case 0: connVariants = theHexTo2Prisms_BT; break;
2450     case 1: connVariants = theHexTo2Prisms_LR; break;
2451     case 2: connVariants = theHexTo2Prisms_FB; break;
2452     default: return method;
2453     }
2454
2455     // look for prisms adjacent via facetToSplit and an opposite one
2456     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2457     {
2458       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2459       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2460       if ( nbNodes != 4 ) return method;
2461
2462       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2463       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2464       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2465       TTriangleFacet* t;
2466       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2467         t = &t012;
2468       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2469         t = &t123;
2470       else
2471         continue;
2472
2473       // there are adjacent prism
2474       for ( int variant = 0; variant < nbVariants; ++variant )
2475       {
2476         // check method compliance with adjacent prisms,
2477         // the found prism facets must be among facets of prisms described by current method
2478         method._nbSplits     = nbSplits;
2479         method._nbCorners    = 6;
2480         method._connectivity = connVariants[ variant ];
2481         if ( method.hasFacet( *t ))
2482           return method;
2483       }
2484     }
2485
2486     // No adjacent prisms. Select a variant with a best aspect ratio.
2487
2488     double badness[2] = { 0., 0. };
2489     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2490     const SMDS_MeshNode** nodes = vol.GetNodes();
2491     for ( int variant = 0; variant < nbVariants; ++variant )
2492       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2493       {
2494         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2495         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2496
2497         method._connectivity = connVariants[ variant ];
2498         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2499         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2500         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2501
2502         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2503                                 nodes[ t->_n2 ],
2504                                 nodes[ t->_n3 ] );
2505         badness[ variant ] += getBadRate( &tria, aspectRatio );
2506       }
2507     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2508
2509     method._nbSplits     = nbSplits;
2510     method._nbCorners    = 6;
2511     method._connectivity = connVariants[ iBetter ];
2512
2513     return method;
2514   }
2515
2516   //================================================================================
2517   /*!
2518    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2519    */
2520   //================================================================================
2521
2522   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2523                                        const SMDSAbs_GeometryType geom ) const
2524   {
2525     // find the tetrahedron including the three nodes of facet
2526     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2527     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2528     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2529     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2530     while ( volIt1->more() )
2531     {
2532       const SMDS_MeshElement* v = volIt1->next();
2533       if ( v->GetGeomType() != geom )
2534         continue;
2535       const int lastCornerInd = v->NbCornerNodes() - 1;
2536       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2537         continue; // medium node not allowed
2538       const int ind2 = v->GetNodeIndex( n2 );
2539       if ( ind2 < 0 || lastCornerInd < ind2 )
2540         continue;
2541       const int ind3 = v->GetNodeIndex( n3 );
2542       if ( ind3 < 0 || lastCornerInd < ind3 )
2543         continue;
2544       return true;
2545     }
2546     return false;
2547   }
2548
2549   //=======================================================================
2550   /*!
2551    * \brief A key of a face of volume
2552    */
2553   //=======================================================================
2554
2555   struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2556   {
2557     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2558     {
2559       TIDSortedNodeSet sortedNodes;
2560       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2561       int nbNodes = vol.NbFaceNodes( iF );
2562       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2563       for ( int i = 0; i < nbNodes; i += iQ )
2564         sortedNodes.insert( fNodes[i] );
2565       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2566       first.first   = (*(n++))->GetID();
2567       first.second  = (*(n++))->GetID();
2568       second.first  = (*(n++))->GetID();
2569       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2570     }
2571   };
2572 } // namespace
2573
2574 //=======================================================================
2575 //function : SplitVolumes
2576 //purpose  : Split volume elements into tetrahedra or prisms.
2577 //           If facet ID < 0, element is split into tetrahedra,
2578 //           else a hexahedron is split into prisms so that the given facet is
2579 //           split into triangles
2580 //=======================================================================
2581
2582 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2583                                      const int            theMethodFlags)
2584 {
2585   SMDS_VolumeTool    volTool;
2586   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2587   fHelper.ToFixNodeParameters( true );
2588
2589   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2590   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2591
2592   SMESH_SequenceOfElemPtr newNodes, newElems;
2593
2594   // map face of volume to it's baricenrtic node
2595   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2596   double bc[3];
2597   vector<const SMDS_MeshElement* > splitVols;
2598
2599   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2600   for ( ; elem2facet != theElems.end(); ++elem2facet )
2601   {
2602     const SMDS_MeshElement* elem = elem2facet->first;
2603     const int       facetToSplit = elem2facet->second;
2604     if ( elem->GetType() != SMDSAbs_Volume )
2605       continue;
2606     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2607     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2608       continue;
2609
2610     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2611
2612     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2613                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2614                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2615     if ( splitMethod._nbSplits < 1 ) continue;
2616
2617     // find submesh to add new tetras to
2618     if ( !subMesh || !subMesh->Contains( elem ))
2619     {
2620       int shapeID = FindShape( elem );
2621       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2622       subMesh = GetMeshDS()->MeshElements( shapeID );
2623     }
2624     int iQ;
2625     if ( elem->IsQuadratic() )
2626     {
2627       iQ = 2;
2628       // add quadratic links to the helper
2629       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2630       {
2631         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2632         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2633         for ( int iN = 0; iN < nbN; iN += iQ )
2634           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2635       }
2636       helper.SetIsQuadratic( true );
2637     }
2638     else
2639     {
2640       iQ = 1;
2641       helper.SetIsQuadratic( false );
2642     }
2643     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2644                                         volTool.GetNodes() + elem->NbNodes() );
2645     helper.SetElementsOnShape( true );
2646     if ( splitMethod._baryNode )
2647     {
2648       // make a node at barycenter
2649       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2650       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2651       nodes.push_back( gcNode );
2652       newNodes.push_back( gcNode );
2653     }
2654     if ( !splitMethod._faceBaryNode.empty() )
2655     {
2656       // make or find baricentric nodes of faces
2657       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2658       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2659       {
2660         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2661           volFace2BaryNode.insert
2662           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2663         if ( !f_n->second )
2664         {
2665           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2666           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2667         }
2668         nodes.push_back( iF_n->second = f_n->second );
2669       }
2670     }
2671
2672     // make new volumes
2673     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2674     const int* volConn = splitMethod._connectivity;
2675     if ( splitMethod._nbCorners == 4 ) // tetra
2676       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2677         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2678                                                                nodes[ volConn[1] ],
2679                                                                nodes[ volConn[2] ],
2680                                                                nodes[ volConn[3] ]));
2681     else // prisms
2682       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2683         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2684                                                                nodes[ volConn[1] ],
2685                                                                nodes[ volConn[2] ],
2686                                                                nodes[ volConn[3] ],
2687                                                                nodes[ volConn[4] ],
2688                                                                nodes[ volConn[5] ]));
2689
2690     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2691
2692     // Split faces on sides of the split volume
2693
2694     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2695     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2696     {
2697       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2698       if ( nbNodes < 4 ) continue;
2699
2700       // find an existing face
2701       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2702                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2703       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2704                                                                        /*noMedium=*/false))
2705       {
2706         // make triangles
2707         helper.SetElementsOnShape( false );
2708         vector< const SMDS_MeshElement* > triangles;
2709
2710         // find submesh to add new triangles in
2711         if ( !fSubMesh || !fSubMesh->Contains( face ))
2712         {
2713           int shapeID = FindShape( face );
2714           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2715         }
2716         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2717         if ( iF_n != splitMethod._faceBaryNode.end() )
2718         {
2719           const SMDS_MeshNode *baryNode = iF_n->second;
2720           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2721           {
2722             const SMDS_MeshNode* n1 = fNodes[iN];
2723             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2724             const SMDS_MeshNode *n3 = baryNode;
2725             if ( !volTool.IsFaceExternal( iF ))
2726               swap( n2, n3 );
2727             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2728           }
2729           if ( fSubMesh ) // update position of the bary node on geometry
2730           {
2731             if ( subMesh )
2732               subMesh->RemoveNode( baryNode );
2733             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2734             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2735             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2736             {
2737               fHelper.SetSubShape( s );
2738               gp_XY uv( 1e100, 1e100 );
2739               double distXYZ[4];
2740               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2741                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2742                    uv.X() < 1e100 )
2743               {
2744                 // node is too far from the surface
2745                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2746                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2747                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2748               }
2749             }
2750           }
2751         }
2752         else
2753         {
2754           // among possible triangles create ones described by split method
2755           const int* nInd = volTool.GetFaceNodesIndices( iF );
2756           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2757           int iCom = 0; // common node of triangle faces to split into
2758           list< TTriangleFacet > facets;
2759           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2760           {
2761             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2762                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2763                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2764             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2765                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2766                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2767             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2768             {
2769               facets.push_back( t012 );
2770               facets.push_back( t023 );
2771               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2772                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2773                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2774                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2775               break;
2776             }
2777           }
2778           list< TTriangleFacet >::iterator facet = facets.begin();
2779           if ( facet == facets.end() )
2780             break;
2781           for ( ; facet != facets.end(); ++facet )
2782           {
2783             if ( !volTool.IsFaceExternal( iF ))
2784               swap( facet->_n2, facet->_n3 );
2785             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2786                                                  volNodes[ facet->_n2 ],
2787                                                  volNodes[ facet->_n3 ]));
2788           }
2789         }
2790         for ( size_t i = 0; i < triangles.size(); ++i )
2791         {
2792           if ( !triangles[ i ]) continue;
2793           if ( fSubMesh )
2794             fSubMesh->AddElement( triangles[ i ]);
2795           newElems.push_back( triangles[ i ]);
2796         }
2797         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2798         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2799
2800       } // while a face based on facet nodes exists
2801     } // loop on volume faces to split them into triangles
2802
2803     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2804
2805     if ( geomType == SMDSEntity_TriQuad_Hexa )
2806     {
2807       // remove medium nodes that could become free
2808       for ( int i = 20; i < volTool.NbNodes(); ++i )
2809         if ( volNodes[i]->NbInverseElements() == 0 )
2810           GetMeshDS()->RemoveNode( volNodes[i] );
2811     }
2812   } // loop on volumes to split
2813
2814   myLastCreatedNodes = newNodes;
2815   myLastCreatedElems = newElems;
2816 }
2817
2818 //=======================================================================
2819 //function : GetHexaFacetsToSplit
2820 //purpose  : For hexahedra that will be split into prisms, finds facets to
2821 //           split into triangles. Only hexahedra adjacent to the one closest
2822 //           to theFacetNormal.Location() are returned.
2823 //param [in,out] theHexas - the hexahedra
2824 //param [in]     theFacetNormal - facet normal
2825 //param [out]    theFacets - the hexahedra and found facet IDs
2826 //=======================================================================
2827
2828 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2829                                              const gp_Ax1&     theFacetNormal,
2830                                              TFacetOfElem &    theFacets)
2831 {
2832 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2833
2834   // Find a hexa closest to the location of theFacetNormal
2835
2836   const SMDS_MeshElement* startHex;
2837   {
2838     // get SMDS_ElemIteratorPtr on theHexas
2839     typedef const SMDS_MeshElement*                                      TValue;
2840     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2841     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2842     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2843     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2844     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2845       ( new TElemSetIter( theHexas.begin(),
2846                           theHexas.end(),
2847                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2848
2849     SMESH_ElementSearcher* searcher =
2850       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2851
2852     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2853
2854     delete searcher;
2855
2856     if ( !startHex )
2857       throw SALOME_Exception( THIS_METHOD "startHex not found");
2858   }
2859
2860   // Select a facet of startHex by theFacetNormal
2861
2862   SMDS_VolumeTool vTool( startHex );
2863   double norm[3], dot, maxDot = 0;
2864   int facetID = -1;
2865   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2866     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2867     {
2868       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2869       if ( dot > maxDot )
2870       {
2871         facetID = iF;
2872         maxDot = dot;
2873       }
2874     }
2875   if ( facetID < 0 )
2876     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2877
2878   // Fill theFacets starting from facetID of startHex
2879
2880   // facets used for searching of volumes adjacent to already treated ones
2881   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2882   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2883   TFacetMap facetsToCheck;
2884
2885   set<const SMDS_MeshNode*> facetNodes;
2886   const SMDS_MeshElement*   curHex;
2887
2888   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2889
2890   while ( startHex )
2891   {
2892     // move in two directions from startHex via facetID
2893     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2894     {
2895       curHex       = startHex;
2896       int curFacet = facetID;
2897       if ( is2nd ) // do not treat startHex twice
2898       {
2899         vTool.Set( curHex );
2900         if ( vTool.IsFreeFace( curFacet, &curHex ))
2901         {
2902           curHex = 0;
2903         }
2904         else
2905         {
2906           vTool.GetFaceNodes( curFacet, facetNodes );
2907           vTool.Set( curHex );
2908           curFacet = vTool.GetFaceIndex( facetNodes );
2909         }
2910       }
2911       while ( curHex )
2912       {
2913         // store a facet to split
2914         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2915         {
2916           theFacets.insert( make_pair( curHex, -1 ));
2917           break;
2918         }
2919         if ( !allHex && !theHexas.count( curHex ))
2920           break;
2921
2922         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2923           theFacets.insert( make_pair( curHex, curFacet ));
2924         if ( !facetIt2isNew.second )
2925           break;
2926
2927         // remember not-to-split facets in facetsToCheck
2928         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2929         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2930         {
2931           if ( iF == curFacet && iF == oppFacet )
2932             continue;
2933           TVolumeFaceKey facetKey ( vTool, iF );
2934           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2935           pair< TFacetMap::iterator, bool > it2isnew =
2936             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2937           if ( !it2isnew.second )
2938             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2939         }
2940         // pass to a volume adjacent via oppFacet
2941         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2942         {
2943           curHex = 0;
2944         }
2945         else
2946         {
2947           // get a new curFacet
2948           vTool.GetFaceNodes( oppFacet, facetNodes );
2949           vTool.Set( curHex );
2950           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2951         }
2952       }
2953     } // move in two directions from startHex via facetID
2954
2955     // Find a new startHex by facetsToCheck
2956
2957     startHex = 0;
2958     facetID  = -1;
2959     TFacetMap::iterator fIt = facetsToCheck.begin();
2960     while ( !startHex && fIt != facetsToCheck.end() )
2961     {
2962       const TElemFacets&  elemFacets = fIt->second;
2963       const SMDS_MeshElement*    hex = elemFacets.first->first;
2964       int                 splitFacet = elemFacets.first->second;
2965       int               lateralFacet = elemFacets.second;
2966       facetsToCheck.erase( fIt );
2967       fIt = facetsToCheck.begin();
2968
2969       vTool.Set( hex );
2970       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2971            curHex->GetGeomType() != SMDSGeom_HEXA )
2972         continue;
2973       if ( !allHex && !theHexas.count( curHex ))
2974         continue;
2975
2976       startHex = curHex;
2977
2978       // find a facet of startHex to split
2979
2980       set<const SMDS_MeshNode*> lateralNodes;
2981       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2982       vTool.GetFaceNodes( splitFacet,   facetNodes );
2983       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2984       vTool.Set( startHex );
2985       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2986
2987       // look for a facet of startHex having common nodes with facetNodes
2988       // but not lateralFacet
2989       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2990       {
2991         if ( iF == lateralFacet )
2992           continue;
2993         int nbCommonNodes = 0;
2994         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2995         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2996           nbCommonNodes += facetNodes.count( nn[ iN ]);
2997
2998         if ( nbCommonNodes >= 2 )
2999         {
3000           facetID = iF;
3001           break;
3002         }
3003       }
3004       if ( facetID < 0 )
3005         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3006     }
3007   } //   while ( startHex )
3008
3009   return;
3010 }
3011
3012 namespace
3013 {
3014   //================================================================================
3015   /*!
3016    * \brief Selects nodes of several elements according to a given interlace
3017    *  \param [in] srcNodes - nodes to select from
3018    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
3019    *  \param [in] interlace - indices of nodes for all elements
3020    *  \param [in] nbElems - nb of elements
3021    *  \param [in] nbNodes - nb of nodes in each element
3022    *  \param [in] mesh - the mesh
3023    *  \param [out] elemQueue - a list to push elements found by the selected nodes
3024    *  \param [in] type - type of elements to look for
3025    */
3026   //================================================================================
3027
3028   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3029                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
3030                     const int*                            interlace,
3031                     const int                             nbElems,
3032                     const int                             nbNodes,
3033                     SMESHDS_Mesh*                         mesh = 0,
3034                     list< const SMDS_MeshElement* >*      elemQueue=0,
3035                     SMDSAbs_ElementType                   type=SMDSAbs_All)
3036   {
3037     for ( int iE = 0; iE < nbElems; ++iE )
3038     {
3039       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3040       const int*                         select = & interlace[iE*nbNodes];
3041       elemNodes.resize( nbNodes );
3042       for ( int iN = 0; iN < nbNodes; ++iN )
3043         elemNodes[iN] = srcNodes[ select[ iN ]];
3044     }
3045     const SMDS_MeshElement* e;
3046     if ( elemQueue )
3047       for ( int iE = 0; iE < nbElems; ++iE )
3048         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3049           elemQueue->push_back( e );
3050   }
3051 }
3052
3053 //=======================================================================
3054 /*
3055  * Split bi-quadratic elements into linear ones without creation of additional nodes
3056  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
3057  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3058  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3059  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
3060  *   will be split in order to keep the mesh conformal.
3061  *  \param elems - elements to split
3062  */
3063 //=======================================================================
3064
3065 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3066 {
3067   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3068   vector<const SMDS_MeshElement* > splitElems;
3069   list< const SMDS_MeshElement* > elemQueue;
3070   list< const SMDS_MeshElement* >::iterator elemIt;
3071
3072   SMESHDS_Mesh * mesh = GetMeshDS();
3073   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3074   int nbElems, nbNodes;
3075
3076   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3077   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3078   {
3079     elemQueue.clear();
3080     elemQueue.push_back( *elemSetIt );
3081     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3082     {
3083       const SMDS_MeshElement* elem = *elemIt;
3084       switch( elem->GetEntityType() )
3085       {
3086       case SMDSEntity_TriQuad_Hexa: // HEX27
3087       {
3088         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3089         nbElems  = nbNodes = 8;
3090         elemType = & hexaType;
3091
3092         // get nodes for new elements
3093         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
3094                                  { 1,9,20,8,    17,22,26,21 },
3095                                  { 2,10,20,9,   18,23,26,22 },
3096                                  { 3,11,20,10,  19,24,26,23 },
3097                                  { 16,21,26,24, 4,12,25,15  },
3098                                  { 17,22,26,21, 5,13,25,12  },
3099                                  { 18,23,26,22, 6,14,25,13  },
3100                                  { 19,24,26,23, 7,15,25,14  }};
3101         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3102
3103         // add boundary faces to elemQueue
3104         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
3105                                  { 4,5,6,7, 12,13,14,15, 25 },
3106                                  { 0,1,5,4, 8,17,12,16,  21 },
3107                                  { 1,2,6,5, 9,18,13,17,  22 },
3108                                  { 2,3,7,6, 10,19,14,18, 23 },
3109                                  { 3,0,4,7, 11,16,15,19, 24 }};
3110         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3111
3112         // add boundary segments to elemQueue
3113         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3114                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3115                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3116         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3117         break;
3118       }
3119       case SMDSEntity_BiQuad_Triangle: // TRIA7
3120       {
3121         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3122         nbElems = 3;
3123         nbNodes = 4;
3124         elemType = & quadType;
3125
3126         // get nodes for new elements
3127         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3128         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3129
3130         // add boundary segments to elemQueue
3131         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3132         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3133         break;
3134       }
3135       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3136       {
3137         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3138         nbElems = 4;
3139         nbNodes = 4;
3140         elemType = & quadType;
3141
3142         // get nodes for new elements
3143         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3144         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3145
3146         // add boundary segments to elemQueue
3147         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3148         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3149         break;
3150       }
3151       case SMDSEntity_Quad_Edge:
3152       {
3153         if ( elemIt == elemQueue.begin() )
3154           continue; // an elem is in theElems
3155         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3156         nbElems = 2;
3157         nbNodes = 2;
3158         elemType = & segType;
3159
3160         // get nodes for new elements
3161         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3162         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3163         break;
3164       }
3165       default: continue;
3166       } // switch( elem->GetEntityType() )
3167
3168       // Create new elements
3169
3170       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3171
3172       splitElems.clear();
3173
3174       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3175       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3176       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3177       //elemType->SetID( -1 );
3178
3179       for ( int iE = 0; iE < nbElems; ++iE )
3180         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3181
3182
3183       ReplaceElemInGroups( elem, splitElems, mesh );
3184
3185       if ( subMesh )
3186         for ( size_t i = 0; i < splitElems.size(); ++i )
3187           subMesh->AddElement( splitElems[i] );
3188     }
3189   }
3190 }
3191
3192 //=======================================================================
3193 //function : AddToSameGroups
3194 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
3195 //=======================================================================
3196
3197 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3198                                         const SMDS_MeshElement* elemInGroups,
3199                                         SMESHDS_Mesh *          aMesh)
3200 {
3201   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3202   if (!groups.empty()) {
3203     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3204     for ( ; grIt != groups.end(); grIt++ ) {
3205       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3206       if ( group && group->Contains( elemInGroups ))
3207         group->SMDSGroup().Add( elemToAdd );
3208     }
3209   }
3210 }
3211
3212
3213 //=======================================================================
3214 //function : RemoveElemFromGroups
3215 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
3216 //=======================================================================
3217 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3218                                              SMESHDS_Mesh *          aMesh)
3219 {
3220   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3221   if (!groups.empty())
3222   {
3223     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3224     for (; GrIt != groups.end(); GrIt++)
3225     {
3226       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3227       if (!grp || grp->IsEmpty()) continue;
3228       grp->SMDSGroup().Remove(removeelem);
3229     }
3230   }
3231 }
3232
3233 //================================================================================
3234 /*!
3235  * \brief Replace elemToRm by elemToAdd in the all groups
3236  */
3237 //================================================================================
3238
3239 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3240                                             const SMDS_MeshElement* elemToAdd,
3241                                             SMESHDS_Mesh *          aMesh)
3242 {
3243   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3244   if (!groups.empty()) {
3245     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3246     for ( ; grIt != groups.end(); grIt++ ) {
3247       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3248       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3249         group->SMDSGroup().Add( elemToAdd );
3250     }
3251   }
3252 }
3253
3254 //================================================================================
3255 /*!
3256  * \brief Replace elemToRm by elemToAdd in the all groups
3257  */
3258 //================================================================================
3259
3260 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
3261                                             const vector<const SMDS_MeshElement*>& elemToAdd,
3262                                             SMESHDS_Mesh *                         aMesh)
3263 {
3264   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3265   if (!groups.empty())
3266   {
3267     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3268     for ( ; grIt != groups.end(); grIt++ ) {
3269       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3270       if ( group && group->SMDSGroup().Remove( elemToRm ) )
3271         for ( size_t i = 0; i < elemToAdd.size(); ++i )
3272           group->SMDSGroup().Add( elemToAdd[ i ] );
3273     }
3274   }
3275 }
3276
3277 //=======================================================================
3278 //function : QuadToTri
3279 //purpose  : Cut quadrangles into triangles.
3280 //           theCrit is used to select a diagonal to cut
3281 //=======================================================================
3282
3283 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3284                                   const bool         the13Diag)
3285 {
3286   ClearLastCreated();
3287   myLastCreatedElems.reserve( theElems.size() * 2 );
3288
3289   SMESHDS_Mesh *       aMesh = GetMeshDS();
3290   Handle(Geom_Surface) surface;
3291   SMESH_MesherHelper   helper( *GetMesh() );
3292
3293   TIDSortedElemSet::iterator itElem;
3294   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3295   {
3296     const SMDS_MeshElement* elem = *itElem;
3297     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3298       continue;
3299
3300     if ( elem->NbNodes() == 4 ) {
3301       // retrieve element nodes
3302       const SMDS_MeshNode* aNodes [4];
3303       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3304       int i = 0;
3305       while ( itN->more() )
3306         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3307
3308       int aShapeId = FindShape( elem );
3309       const SMDS_MeshElement* newElem1 = 0;
3310       const SMDS_MeshElement* newElem2 = 0;
3311       if ( the13Diag ) {
3312         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3313         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3314       }
3315       else {
3316         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3317         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3318       }
3319       myLastCreatedElems.push_back(newElem1);
3320       myLastCreatedElems.push_back(newElem2);
3321       // put a new triangle on the same shape and add to the same groups
3322       if ( aShapeId )
3323       {
3324         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3325         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3326       }
3327       AddToSameGroups( newElem1, elem, aMesh );
3328       AddToSameGroups( newElem2, elem, aMesh );
3329       aMesh->RemoveElement( elem );
3330     }
3331
3332     // Quadratic quadrangle
3333
3334     else if ( elem->NbNodes() >= 8 )
3335     {
3336       // get surface elem is on
3337       int aShapeId = FindShape( elem );
3338       if ( aShapeId != helper.GetSubShapeID() ) {
3339         surface.Nullify();
3340         TopoDS_Shape shape;
3341         if ( aShapeId > 0 )
3342           shape = aMesh->IndexToShape( aShapeId );
3343         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3344           TopoDS_Face face = TopoDS::Face( shape );
3345           surface = BRep_Tool::Surface( face );
3346           if ( !surface.IsNull() )
3347             helper.SetSubShape( shape );
3348         }
3349       }
3350
3351       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3352       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3353       for ( int i = 0; itN->more(); ++i )
3354         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3355
3356       const SMDS_MeshNode* centrNode = aNodes[8];
3357       if ( centrNode == 0 )
3358       {
3359         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3360                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3361                                            surface.IsNull() );
3362         myLastCreatedNodes.push_back(centrNode);
3363       }
3364
3365       // create a new element
3366       const SMDS_MeshElement* newElem1 = 0;
3367       const SMDS_MeshElement* newElem2 = 0;
3368       if ( the13Diag ) {
3369         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3370                                   aNodes[6], aNodes[7], centrNode );
3371         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3372                                   centrNode, aNodes[4], aNodes[5] );
3373       }
3374       else {
3375         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3376                                   aNodes[7], aNodes[4], centrNode );
3377         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3378                                   centrNode, aNodes[5], aNodes[6] );
3379       }
3380       myLastCreatedElems.push_back(newElem1);
3381       myLastCreatedElems.push_back(newElem2);
3382       // put a new triangle on the same shape and add to the same groups
3383       if ( aShapeId )
3384       {
3385         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3386         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3387       }
3388       AddToSameGroups( newElem1, elem, aMesh );
3389       AddToSameGroups( newElem2, elem, aMesh );
3390       aMesh->RemoveElement( elem );
3391     }
3392   }
3393
3394   return true;
3395 }
3396
3397 //=======================================================================
3398 //function : getAngle
3399 //purpose  :
3400 //=======================================================================
3401
3402 double getAngle(const SMDS_MeshElement * tr1,
3403                 const SMDS_MeshElement * tr2,
3404                 const SMDS_MeshNode *    n1,
3405                 const SMDS_MeshNode *    n2)
3406 {
3407   double angle = 2. * M_PI; // bad angle
3408
3409   // get normals
3410   SMESH::Controls::TSequenceOfXYZ P1, P2;
3411   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3412        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3413     return angle;
3414   gp_Vec N1,N2;
3415   if(!tr1->IsQuadratic())
3416     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3417   else
3418     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3419   if ( N1.SquareMagnitude() <= gp::Resolution() )
3420     return angle;
3421   if(!tr2->IsQuadratic())
3422     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3423   else
3424     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3425   if ( N2.SquareMagnitude() <= gp::Resolution() )
3426     return angle;
3427
3428   // find the first diagonal node n1 in the triangles:
3429   // take in account a diagonal link orientation
3430   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3431   for ( int t = 0; t < 2; t++ ) {
3432     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3433     int i = 0, iDiag = -1;
3434     while ( it->more()) {
3435       const SMDS_MeshElement *n = it->next();
3436       if ( n == n1 || n == n2 ) {
3437         if ( iDiag < 0)
3438           iDiag = i;
3439         else {
3440           if ( i - iDiag == 1 )
3441             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3442           else
3443             nFirst[ t ] = n;
3444           break;
3445         }
3446       }
3447       i++;
3448     }
3449   }
3450   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3451     N2.Reverse();
3452
3453   angle = N1.Angle( N2 );
3454   //SCRUTE( angle );
3455   return angle;
3456 }
3457
3458 // =================================================
3459 // class generating a unique ID for a pair of nodes
3460 // and able to return nodes by that ID
3461 // =================================================
3462 class LinkID_Gen {
3463 public:
3464
3465   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3466     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3467   {}
3468
3469   smIdType GetLinkID (const SMDS_MeshNode * n1,
3470                   const SMDS_MeshNode * n2) const
3471   {
3472     return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3473   }
3474
3475   bool GetNodes (const long             theLinkID,
3476                  const SMDS_MeshNode* & theNode1,
3477                  const SMDS_MeshNode* & theNode2) const
3478   {
3479     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3480     if ( !theNode1 ) return false;
3481     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3482     if ( !theNode2 ) return false;
3483     return true;
3484   }
3485
3486 private:
3487   LinkID_Gen();
3488   const SMESHDS_Mesh* myMesh;
3489   long                myMaxID;
3490 };
3491
3492
3493 //=======================================================================
3494 //function : TriToQuad
3495 //purpose  : Fuse neighbour triangles into quadrangles.
3496 //           theCrit is used to select a neighbour to fuse with.
3497 //           theMaxAngle is a max angle between element normals at which
3498 //           fusion is still performed.
3499 //=======================================================================
3500
3501 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3502                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3503                                   const double                         theMaxAngle)
3504 {
3505   ClearLastCreated();
3506   myLastCreatedElems.reserve( theElems.size() / 2 );
3507
3508   if ( !theCrit.get() )
3509     return false;
3510
3511   SMESHDS_Mesh * aMesh = GetMeshDS();
3512
3513   // Prepare data for algo: build
3514   // 1. map of elements with their linkIDs
3515   // 2. map of linkIDs with their elements
3516
3517   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3518   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3519   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3520   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3521
3522   TIDSortedElemSet::iterator itElem;
3523   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3524   {
3525     const SMDS_MeshElement* elem = *itElem;
3526     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3527     bool IsTria = ( elem->NbCornerNodes()==3 );
3528     if (!IsTria) continue;
3529
3530     // retrieve element nodes
3531     const SMDS_MeshNode* aNodes [4];
3532     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3533     int i = 0;
3534     while ( i < 3 )
3535       aNodes[ i++ ] = itN->next();
3536     aNodes[ 3 ] = aNodes[ 0 ];
3537
3538     // fill maps
3539     for ( i = 0; i < 3; i++ ) {
3540       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3541       // check if elements sharing a link can be fused
3542       itLE = mapLi_listEl.find( link );
3543       if ( itLE != mapLi_listEl.end() ) {
3544         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3545           continue;
3546         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3547         //if ( FindShape( elem ) != FindShape( elem2 ))
3548         //  continue; // do not fuse triangles laying on different shapes
3549         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3550           continue; // avoid making badly shaped quads
3551         (*itLE).second.push_back( elem );
3552       }
3553       else {
3554         mapLi_listEl[ link ].push_back( elem );
3555       }
3556       mapEl_setLi [ elem ].insert( link );
3557     }
3558   }
3559   // Clean the maps from the links shared by a sole element, ie
3560   // links to which only one element is bound in mapLi_listEl
3561
3562   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3563     int nbElems = (*itLE).second.size();
3564     if ( nbElems < 2  ) {
3565       const SMDS_MeshElement* elem = (*itLE).second.front();
3566       SMESH_TLink link = (*itLE).first;
3567       mapEl_setLi[ elem ].erase( link );
3568       if ( mapEl_setLi[ elem ].empty() )
3569         mapEl_setLi.erase( elem );
3570     }
3571   }
3572
3573   // Algo: fuse triangles into quadrangles
3574
3575   while ( ! mapEl_setLi.empty() ) {
3576     // Look for the start element:
3577     // the element having the least nb of shared links
3578     const SMDS_MeshElement* startElem = 0;
3579     int minNbLinks = 4;
3580     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3581       int nbLinks = (*itEL).second.size();
3582       if ( nbLinks < minNbLinks ) {
3583         startElem = (*itEL).first;
3584         minNbLinks = nbLinks;
3585         if ( minNbLinks == 1 )
3586           break;
3587       }
3588     }
3589
3590     // search elements to fuse starting from startElem or links of elements
3591     // fused earlyer - startLinks
3592     list< SMESH_TLink > startLinks;
3593     while ( startElem || !startLinks.empty() ) {
3594       while ( !startElem && !startLinks.empty() ) {
3595         // Get an element to start, by a link
3596         SMESH_TLink linkId = startLinks.front();
3597         startLinks.pop_front();
3598         itLE = mapLi_listEl.find( linkId );
3599         if ( itLE != mapLi_listEl.end() ) {
3600           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3601           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3602           for ( ; itE != listElem.end() ; itE++ )
3603             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3604               startElem = (*itE);
3605           mapLi_listEl.erase( itLE );
3606         }
3607       }
3608
3609       if ( startElem ) {
3610         // Get candidates to be fused
3611         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3612         const SMESH_TLink *link12 = 0, *link13 = 0;
3613         startElem = 0;
3614         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3615         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3616         ASSERT( !setLi.empty() );
3617         set< SMESH_TLink >::iterator itLi;
3618         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3619         {
3620           const SMESH_TLink & link = (*itLi);
3621           itLE = mapLi_listEl.find( link );
3622           if ( itLE == mapLi_listEl.end() )
3623             continue;
3624
3625           const SMDS_MeshElement* elem = (*itLE).second.front();
3626           if ( elem == tr1 )
3627             elem = (*itLE).second.back();
3628           mapLi_listEl.erase( itLE );
3629           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3630             continue;
3631           if ( tr2 ) {
3632             tr3 = elem;
3633             link13 = &link;
3634           }
3635           else {
3636             tr2 = elem;
3637             link12 = &link;
3638           }
3639
3640           // add other links of elem to list of links to re-start from
3641           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3642           set< SMESH_TLink >::iterator it;
3643           for ( it = links.begin(); it != links.end(); it++ ) {
3644             const SMESH_TLink& link2 = (*it);
3645             if ( link2 != link )
3646               startLinks.push_back( link2 );
3647           }
3648         }
3649
3650         // Get nodes of possible quadrangles
3651         const SMDS_MeshNode *n12 [4], *n13 [4];
3652         bool Ok12 = false, Ok13 = false;
3653         const SMDS_MeshNode *linkNode1, *linkNode2;
3654         if(tr2) {
3655           linkNode1 = link12->first;
3656           linkNode2 = link12->second;
3657           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3658             Ok12 = true;
3659         }
3660         if(tr3) {
3661           linkNode1 = link13->first;
3662           linkNode2 = link13->second;
3663           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3664             Ok13 = true;
3665         }
3666
3667         // Choose a pair to fuse
3668         if ( Ok12 && Ok13 ) {
3669           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3670           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3671           double aBadRate12 = getBadRate( &quad12, theCrit );
3672           double aBadRate13 = getBadRate( &quad13, theCrit );
3673           if (  aBadRate13 < aBadRate12 )
3674             Ok12 = false;
3675           else
3676             Ok13 = false;
3677         }
3678
3679         // Make quadrangles
3680         // and remove fused elems and remove links from the maps
3681         mapEl_setLi.erase( tr1 );
3682         if ( Ok12 )
3683         {
3684           mapEl_setLi.erase( tr2 );
3685           mapLi_listEl.erase( *link12 );
3686           if ( tr1->NbNodes() == 3 )
3687           {
3688             const SMDS_MeshElement* newElem = 0;
3689             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3690             myLastCreatedElems.push_back(newElem);
3691             AddToSameGroups( newElem, tr1, aMesh );
3692             int aShapeId = tr1->getshapeId();
3693             if ( aShapeId )
3694               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3695             aMesh->RemoveElement( tr1 );
3696             aMesh->RemoveElement( tr2 );
3697           }
3698           else {
3699             vector< const SMDS_MeshNode* > N1;
3700             vector< const SMDS_MeshNode* > N2;
3701             getNodesFromTwoTria(tr1,tr2,N1,N2);
3702             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3703             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3704             // i.e. first nodes from both arrays form a new diagonal
3705             const SMDS_MeshNode* aNodes[8];
3706             aNodes[0] = N1[0];
3707             aNodes[1] = N1[1];
3708             aNodes[2] = N2[0];
3709             aNodes[3] = N2[1];
3710             aNodes[4] = N1[3];
3711             aNodes[5] = N2[5];
3712             aNodes[6] = N2[3];
3713             aNodes[7] = N1[5];
3714             const SMDS_MeshElement* newElem = 0;
3715             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3716               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3717                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3718             else
3719               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3720                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3721             myLastCreatedElems.push_back(newElem);
3722             AddToSameGroups( newElem, tr1, aMesh );
3723             int aShapeId = tr1->getshapeId();
3724             if ( aShapeId )
3725               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3726             aMesh->RemoveElement( tr1 );
3727             aMesh->RemoveElement( tr2 );
3728             // remove middle node (9)
3729             if ( N1[4]->NbInverseElements() == 0 )
3730               aMesh->RemoveNode( N1[4] );
3731             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3732               aMesh->RemoveNode( N1[6] );
3733             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3734               aMesh->RemoveNode( N2[6] );
3735           }
3736         }
3737         else if ( Ok13 )
3738         {
3739           mapEl_setLi.erase( tr3 );
3740           mapLi_listEl.erase( *link13 );
3741           if ( tr1->NbNodes() == 3 ) {
3742             const SMDS_MeshElement* newElem = 0;
3743             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3744             myLastCreatedElems.push_back(newElem);
3745             AddToSameGroups( newElem, tr1, aMesh );
3746             int aShapeId = tr1->getshapeId();
3747             if ( aShapeId )
3748               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3749             aMesh->RemoveElement( tr1 );
3750             aMesh->RemoveElement( tr3 );
3751           }
3752           else {
3753             vector< const SMDS_MeshNode* > N1;
3754             vector< const SMDS_MeshNode* > N2;
3755             getNodesFromTwoTria(tr1,tr3,N1,N2);
3756             // now we receive following N1 and N2 (using numeration as above image)
3757             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3758             // i.e. first nodes from both arrays form a new diagonal
3759             const SMDS_MeshNode* aNodes[8];
3760             aNodes[0] = N1[0];
3761             aNodes[1] = N1[1];
3762             aNodes[2] = N2[0];
3763             aNodes[3] = N2[1];
3764             aNodes[4] = N1[3];
3765             aNodes[5] = N2[5];
3766             aNodes[6] = N2[3];
3767             aNodes[7] = N1[5];
3768             const SMDS_MeshElement* newElem = 0;
3769             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3770               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3771                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3772             else
3773               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3774                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3775             myLastCreatedElems.push_back(newElem);
3776             AddToSameGroups( newElem, tr1, aMesh );
3777             int aShapeId = tr1->getshapeId();
3778             if ( aShapeId )
3779               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3780             aMesh->RemoveElement( tr1 );
3781             aMesh->RemoveElement( tr3 );
3782             // remove middle node (9)
3783             if ( N1[4]->NbInverseElements() == 0 )
3784               aMesh->RemoveNode( N1[4] );
3785             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3786               aMesh->RemoveNode( N1[6] );
3787             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3788               aMesh->RemoveNode( N2[6] );
3789           }
3790         }
3791
3792         // Next element to fuse: the rejected one
3793         if ( tr3 )
3794           startElem = Ok12 ? tr3 : tr2;
3795
3796       } // if ( startElem )
3797     } // while ( startElem || !startLinks.empty() )
3798   } // while ( ! mapEl_setLi.empty() )
3799
3800   return true;
3801 }
3802
3803 //================================================================================
3804 /*!
3805  * \brief Return nodes linked to the given one
3806  * \param theNode - the node
3807  * \param linkedNodes - the found nodes
3808  * \param type - the type of elements to check
3809  *
3810  * Medium nodes are ignored
3811  */
3812 //================================================================================
3813
3814 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3815                                        TIDSortedElemSet &   linkedNodes,
3816                                        SMDSAbs_ElementType  type )
3817 {
3818   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3819   while ( elemIt->more() )
3820   {
3821     const SMDS_MeshElement* elem = elemIt->next();
3822     if(elem->GetType() == SMDSAbs_0DElement)
3823       continue;
3824
3825     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3826     if ( elem->GetType() == SMDSAbs_Volume )
3827     {
3828       SMDS_VolumeTool vol( elem );
3829       while ( nodeIt->more() ) {
3830         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3831         if ( theNode != n && vol.IsLinked( theNode, n ))
3832           linkedNodes.insert( n );
3833       }
3834     }
3835     else
3836     {
3837       for ( int i = 0; nodeIt->more(); ++i ) {
3838         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3839         if ( n == theNode ) {
3840           int iBefore = i - 1;
3841           int iAfter  = i + 1;
3842           if ( elem->IsQuadratic() ) {
3843             int nb = elem->NbNodes() / 2;
3844             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3845             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3846           }
3847           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3848           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3849         }
3850       }
3851     }
3852   }
3853 }
3854
3855 //=======================================================================
3856 //function : averageBySurface
3857 //purpose  : Auxiliar function to treat properly nodes in periodic faces in the laplacian smoother
3858 //=======================================================================
3859 void averageBySurface( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, 
3860                         TIDSortedElemSet& nodeSet, map< const SMDS_MeshNode*, gp_XY* >& theUVMap, double * coord )
3861 {
3862   if ( theSurface.IsNull() ) 
3863   {
3864     TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3865     for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) 
3866     {
3867       const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3868       coord[0] += node->X();
3869       coord[1] += node->Y();
3870       coord[2] += node->Z();
3871     }
3872   }
3873   else
3874   {
3875     Standard_Real Umin,Umax,Vmin,Vmax;
3876     theSurface->Bounds( Umin, Umax, Vmin, Vmax );
3877     ASSERT( theUVMap.find( refNode ) != theUVMap.end() );
3878     gp_XY* nodeUV = theUVMap[ refNode ];
3879     Standard_Real uref = nodeUV->X();
3880     Standard_Real vref = nodeUV->Y();
3881
3882     TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3883     for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) 
3884     {
3885       const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3886       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3887       gp_XY* uv = theUVMap[ node ];    
3888
3889       if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )  
3890       {          
3891         Standard_Real u          = uv->X();
3892         Standard_Real v          = uv->Y();                      
3893         Standard_Real uCorrected = u;
3894         Standard_Real vCorrected = v;
3895         bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3896         bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3897
3898         if( isUTobeCorrected  )
3899           uCorrected = uref > u ? Umax + std::fabs(Umin - u) : Umin - std::fabs(Umax - u);
3900
3901         if( isVTobeCorrected )
3902           vCorrected = vref > v ? Vmax + std::fabs(Vmin - v) : Vmin - std::fabs(Vmax - v);
3903         
3904         coord[0] += uCorrected;
3905         coord[1] += vCorrected;
3906
3907       }
3908       else
3909       {
3910         coord[0] += uv->X();
3911         coord[1] += uv->Y();
3912       }
3913     }   
3914   }
3915 }
3916
3917 //=======================================================================
3918 //function : laplacianSmooth
3919 //purpose  : pulls theNode toward the center of surrounding nodes directly
3920 //           connected to that node along an element edge
3921 //=======================================================================
3922
3923 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3924                      const Handle(Geom_Surface)&          theSurface,
3925                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3926 {
3927   // find surrounding nodes
3928
3929   TIDSortedElemSet nodeSet;
3930   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3931
3932   // compute new coodrs
3933   double coord[] = { 0., 0., 0. };  
3934
3935   averageBySurface( theSurface, theNode, nodeSet, theUVMap, coord );
3936
3937   int nbNodes = nodeSet.size();
3938   if ( !nbNodes )
3939     return;
3940
3941   coord[0] /= nbNodes;
3942   coord[1] /= nbNodes;
3943
3944   if ( !theSurface.IsNull() ) {
3945     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3946     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3947     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3948     coord[0] = p3d.X();
3949     coord[1] = p3d.Y();
3950     coord[2] = p3d.Z();    
3951   }
3952   else
3953     coord[2] /= nbNodes;
3954
3955   // move node
3956
3957   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3958 }
3959
3960 //=======================================================================
3961 //function : correctTheValue
3962 //purpose  : Given a boundaries of parametric space determine if the node coordinate (u,v) need correction 
3963 //            based on the reference coordinate (uref,vref)
3964 //=======================================================================
3965 void correctTheValue( Standard_Real Umax, Standard_Real Umin, Standard_Real Vmax, Standard_Real Vmin, 
3966                         Standard_Real uref, Standard_Real vref, Standard_Real &u, Standard_Real &v  )
3967 {
3968   bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3969   bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3970   if ( isUTobeCorrected )
3971     u = std::fabs(u-Umin) < 1e-7 ? Umax : Umin;            
3972   if ( isVTobeCorrected )
3973     v = std::fabs(v-Vmin) < 1e-7 ? Vmax : Vmin;
3974 }
3975
3976 //=======================================================================
3977 //function : averageByElement
3978 //purpose  : Auxiliar function to treat properly nodes in periodic faces in the centroidal smoother
3979 //=======================================================================
3980 void averageByElement( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, const SMDS_MeshElement* elem,
3981                         map< const SMDS_MeshNode*, gp_XY* >& theUVMap, SMESH::Controls::TSequenceOfXYZ& aNodePoints, 
3982                         gp_XYZ& elemCenter )
3983 {
3984   int nn = elem->NbNodes();
3985   if(elem->IsQuadratic()) nn = nn/2;
3986   int i=0;
3987   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3988   Standard_Real Umin,Umax,Vmin,Vmax;
3989   while ( i<nn ) 
3990   {
3991     const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3992     i++;
3993     gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3994     aNodePoints.push_back( aP );
3995     if ( !theSurface.IsNull() ) // smooth in 2D
3996     { 
3997       ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3998       gp_XY* uv = theUVMap[ aNode ];
3999
4000       if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )  
4001       {  
4002         theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4003         Standard_Real u          = uv->X();
4004         Standard_Real v          = uv->Y();   
4005         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;
4006         if ( !isSingularPoint )
4007         {
4008           aP.SetCoord( uv->X(), uv->Y(), 0. );
4009         }
4010         else
4011         {
4012           gp_XY* refPoint = theUVMap[ refNode ];
4013           Standard_Real uref = refPoint->X();
4014           Standard_Real vref = refPoint->Y();
4015           correctTheValue( Umax, Umin, Vmax, Vmin, uref, vref, u, v ); 
4016           aP.SetCoord( u, v, 0. );
4017         }        
4018       }
4019       else
4020         aP.SetCoord( uv->X(), uv->Y(), 0. );
4021     }    
4022     elemCenter += aP;   
4023   }
4024 }
4025
4026 //=======================================================================
4027 //function : centroidalSmooth
4028 //purpose  : pulls theNode toward the element-area-weighted centroid of the
4029 //           surrounding elements
4030 //=======================================================================
4031
4032 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
4033                       const Handle(Geom_Surface)&          theSurface,
4034                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
4035 {
4036   gp_XYZ aNewXYZ(0.,0.,0.);
4037   SMESH::Controls::Area anAreaFunc;
4038   double totalArea = 0.;
4039   int nbElems = 0;
4040   // compute new XYZ
4041   bool notToMoveNode = false;
4042   // Do not correct singular nodes
4043   if ( !theSurface.IsNull() && (theSurface->IsUPeriodic() || theSurface->IsVPeriodic()) )
4044   { 
4045     Standard_Real Umin,Umax,Vmin,Vmax;
4046     theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4047     gp_XY* uv = theUVMap[ theNode ];
4048     Standard_Real u = uv->X();
4049     Standard_Real v = uv->Y();   
4050     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;
4051   }
4052   
4053   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
4054   while ( elemIt->more() && !notToMoveNode )
4055   {
4056     const SMDS_MeshElement* elem = elemIt->next();
4057     nbElems++;
4058
4059     gp_XYZ elemCenter(0.,0.,0.);
4060     SMESH::Controls::TSequenceOfXYZ aNodePoints;
4061     int nn = elem->NbNodes();
4062     if(elem->IsQuadratic()) nn = nn/2;
4063     averageByElement( theSurface, theNode, elem, theUVMap, aNodePoints, elemCenter );
4064
4065     double elemArea = anAreaFunc.GetValue( aNodePoints );
4066     totalArea += elemArea;
4067     elemCenter /= nn;
4068     aNewXYZ += elemCenter * elemArea;
4069   }
4070   aNewXYZ /= totalArea;
4071   
4072   if ( !theSurface.IsNull() && !notToMoveNode ) {
4073     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
4074     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
4075   }
4076
4077   // move node
4078   if ( !notToMoveNode )
4079     const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
4080 }
4081
4082 //=======================================================================
4083 //function : getClosestUV
4084 //purpose  : return UV of closest projection
4085 //=======================================================================
4086
4087 static bool getClosestUV (Extrema_GenExtPS& projector,
4088                           const gp_Pnt&     point,
4089                           gp_XY &           result)
4090 {
4091   projector.Perform( point );
4092   if ( projector.IsDone() ) {
4093     double u = 0, v = 0, minVal = DBL_MAX;
4094     for ( int i = projector.NbExt(); i > 0; i-- )
4095       if ( projector.SquareDistance( i ) < minVal ) {
4096         minVal = projector.SquareDistance( i );
4097         projector.Point( i ).Parameter( u, v );
4098       }
4099     result.SetCoord( u, v );
4100     return true;
4101   }
4102   return false;
4103 }
4104
4105 //=======================================================================
4106 //function : Smooth
4107 //purpose  : Smooth theElements during theNbIterations or until a worst
4108 //           element has aspect ratio <= theTgtAspectRatio.
4109 //           Aspect Ratio varies in range [1.0, inf].
4110 //           If theElements is empty, the whole mesh is smoothed.
4111 //           theFixedNodes contains additionally fixed nodes. Nodes built
4112 //           on edges and boundary nodes are always fixed.
4113 //=======================================================================
4114
4115 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
4116                                set<const SMDS_MeshNode*> & theFixedNodes,
4117                                const SmoothMethod          theSmoothMethod,
4118                                const int                   theNbIterations,
4119                                double                      theTgtAspectRatio,
4120                                const bool                  the2D)
4121 {
4122   ClearLastCreated();
4123
4124   if ( theTgtAspectRatio < 1.0 )
4125     theTgtAspectRatio = 1.0;
4126
4127   const double disttol = 1.e-16;
4128
4129   SMESH::Controls::AspectRatio aQualityFunc;
4130
4131   SMESHDS_Mesh* aMesh = GetMeshDS();
4132
4133   if ( theElems.empty() ) {
4134     // add all faces to theElems
4135     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4136     while ( fIt->more() ) {
4137       const SMDS_MeshElement* face = fIt->next();
4138       theElems.insert( theElems.end(), face );
4139     }
4140   }
4141   // get all face ids theElems are on
4142   set< int > faceIdSet;
4143   TIDSortedElemSet::iterator itElem;
4144   if ( the2D )
4145     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4146       int fId = FindShape( *itElem );
4147       // check that corresponding submesh exists and a shape is face
4148       if (fId &&
4149           faceIdSet.find( fId ) == faceIdSet.end() &&
4150           aMesh->MeshElements( fId )) {
4151         TopoDS_Shape F = aMesh->IndexToShape( fId );
4152         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4153           faceIdSet.insert( fId );
4154       }
4155     }
4156   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4157
4158   // ===============================================
4159   // smooth elements on each TopoDS_Face separately
4160   // ===============================================
4161
4162   SMESH_MesherHelper helper( *GetMesh() );
4163
4164   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4165   for ( ; fId != faceIdSet.rend(); ++fId )
4166   {
4167     // get face surface and submesh
4168     Handle(Geom_Surface) surface;
4169     SMESHDS_SubMesh* faceSubMesh = 0;
4170     TopoDS_Face face;
4171     double fToler2 = 0;
4172     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4173     bool isUPeriodic = false, isVPeriodic = false;
4174     if ( *fId )
4175     {
4176       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4177       surface = BRep_Tool::Surface( face );
4178       faceSubMesh = aMesh->MeshElements( *fId );
4179       fToler2 = BRep_Tool::Tolerance( face );
4180       fToler2 *= fToler2 * 10.;
4181       isUPeriodic = surface->IsUPeriodic();
4182       // if ( isUPeriodic )
4183       //   surface->UPeriod();
4184       isVPeriodic = surface->IsVPeriodic();
4185       // if ( isVPeriodic )
4186       //   surface->VPeriod();
4187       surface->Bounds( u1, u2, v1, v2 );
4188       helper.SetSubShape( face );
4189     }
4190     // ---------------------------------------------------------
4191     // for elements on a face, find movable and fixed nodes and
4192     // compute UV for them
4193     // ---------------------------------------------------------
4194     bool checkBoundaryNodes = false;
4195     bool isQuadratic = false;
4196     set<const SMDS_MeshNode*> setMovableNodes;
4197     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4198     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4199     list< const SMDS_MeshElement* > elemsOnFace;
4200
4201     Extrema_GenExtPS projector;
4202     GeomAdaptor_Surface surfAdaptor;
4203     if ( !surface.IsNull() ) {
4204       surfAdaptor.Load( surface );
4205       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4206     }
4207     int nbElemOnFace = 0;
4208     itElem = theElems.begin();
4209     // loop on not yet smoothed elements: look for elems on a face
4210     while ( itElem != theElems.end() )
4211     {
4212       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4213         break; // all elements found
4214
4215       const SMDS_MeshElement* elem = *itElem;
4216       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4217            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4218         ++itElem;
4219         continue;
4220       }
4221       elemsOnFace.push_back( elem );
4222       theElems.erase( itElem++ );
4223       nbElemOnFace++;
4224
4225       if ( !isQuadratic )
4226         isQuadratic = elem->IsQuadratic();
4227
4228       // get movable nodes of elem
4229       const SMDS_MeshNode* node;
4230       SMDS_TypeOfPosition posType;
4231       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4232       int nn = 0, nbn =  elem->NbNodes();
4233       if(elem->IsQuadratic())
4234         nbn = nbn/2;
4235       while ( nn++ < nbn ) {
4236         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4237         const SMDS_PositionPtr& pos = node->GetPosition();
4238         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4239         if (posType != SMDS_TOP_EDGE &&
4240             posType != SMDS_TOP_VERTEX &&
4241             theFixedNodes.find( node ) == theFixedNodes.end())
4242         {
4243           // check if all faces around the node are on faceSubMesh
4244           // because a node on edge may be bound to face
4245           bool all = true;
4246           if ( faceSubMesh ) {
4247             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4248             while ( eIt->more() && all ) {
4249               const SMDS_MeshElement* e = eIt->next();
4250               all = faceSubMesh->Contains( e );
4251             }
4252           }
4253           if ( all )
4254             setMovableNodes.insert( node );
4255           else
4256             checkBoundaryNodes = true;
4257         }
4258         if ( posType == SMDS_TOP_3DSPACE )
4259           checkBoundaryNodes = true;
4260       }
4261
4262       if ( surface.IsNull() )
4263         continue;
4264
4265       // get nodes to check UV
4266       list< const SMDS_MeshNode* > uvCheckNodes;
4267       const SMDS_MeshNode* nodeInFace = 0;
4268       itN = elem->nodesIterator();
4269       nn = 0; nbn =  elem->NbNodes();
4270       if(elem->IsQuadratic())
4271         nbn = nbn/2;
4272       while ( nn++ < nbn ) {
4273         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4274         if ( node->GetPosition()->GetDim() == 2 )
4275           nodeInFace = node;
4276         if ( uvMap.find( node ) == uvMap.end() )
4277           uvCheckNodes.push_back( node );
4278         // add nodes of elems sharing node
4279         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4280         //         while ( eIt->more() ) {
4281         //           const SMDS_MeshElement* e = eIt->next();
4282         //           if ( e != elem ) {
4283         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4284         //             while ( nIt->more() ) {
4285         //               const SMDS_MeshNode* n =
4286         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4287         //               if ( uvMap.find( n ) == uvMap.end() )
4288         //                 uvCheckNodes.push_back( n );
4289         //             }
4290         //           }
4291         //         }
4292       }
4293       // check UV on face
4294       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4295       for ( ; n != uvCheckNodes.end(); ++n ) {
4296         node = *n;
4297         gp_XY uv( 0, 0 );
4298         const SMDS_PositionPtr& pos = node->GetPosition();
4299         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4300         // get existing UV
4301         if ( pos )
4302         {
4303           bool toCheck = true;
4304           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4305         }
4306         // compute not existing UV
4307         bool project = ( posType == SMDS_TOP_3DSPACE );
4308         // double dist1 = DBL_MAX, dist2 = 0;
4309         // if ( posType != SMDS_TOP_3DSPACE ) {
4310         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4311         //   project = dist1 > fToler2;
4312         // }
4313         if ( project ) { // compute new UV
4314           gp_XY newUV;
4315           gp_Pnt pNode = SMESH_NodeXYZ( node );
4316           if ( !getClosestUV( projector, pNode, newUV )) {
4317             MESSAGE("Node Projection Failed " << node);
4318           }
4319           else {
4320             if ( isUPeriodic )
4321               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4322             if ( isVPeriodic )
4323               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4324             // check new UV
4325             // if ( posType != SMDS_TOP_3DSPACE )
4326             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4327             // if ( dist2 < dist1 )
4328             uv = newUV;
4329           }
4330         }
4331         // store UV in the map
4332         listUV.push_back( uv );
4333         uvMap.insert( make_pair( node, &listUV.back() ));
4334       }
4335     } // loop on not yet smoothed elements
4336
4337     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4338       checkBoundaryNodes = true;
4339
4340     // fix nodes on mesh boundary
4341
4342     if ( checkBoundaryNodes ) {
4343       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4344       map< SMESH_TLink, int >::iterator link_nb;
4345       // put all elements links to linkNbMap
4346       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4347       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4348         const SMDS_MeshElement* elem = (*elemIt);
4349         int nbn =  elem->NbCornerNodes();
4350         // loop on elem links: insert them in linkNbMap
4351         for ( int iN = 0; iN < nbn; ++iN ) {
4352           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4353           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4354           SMESH_TLink link( n1, n2 );
4355           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4356           link_nb->second++;
4357         }
4358       }
4359       // remove nodes that are in links encountered only once from setMovableNodes
4360       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4361         if ( link_nb->second == 1 ) {
4362           setMovableNodes.erase( link_nb->first.node1() );
4363           setMovableNodes.erase( link_nb->first.node2() );
4364         }
4365       }
4366     }
4367
4368     // -----------------------------------------------------
4369     // for nodes on seam edge, compute one more UV ( uvMap2 );
4370     // find movable nodes linked to nodes on seam and which
4371     // are to be smoothed using the second UV ( uvMap2 )
4372     // -----------------------------------------------------
4373
4374     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4375     if ( !surface.IsNull() ) {
4376       TopExp_Explorer eExp( face, TopAbs_EDGE );
4377       for ( ; eExp.More(); eExp.Next() ) {
4378         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4379         if ( !BRep_Tool::IsClosed( edge, face ))
4380           continue;
4381         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4382         if ( !sm )
4383           continue;
4384         // find out which parameter varies for a node on seam
4385         double f,l;
4386         gp_Pnt2d uv1, uv2;
4387         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4388         if ( pcurve.IsNull() ) continue;
4389         uv1 = pcurve->Value( f );
4390         edge.Reverse();
4391         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4392         if ( pcurve.IsNull() ) continue;
4393         uv2 = pcurve->Value( f );
4394         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4395         // assure uv1 < uv2
4396         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4397           std::swap( uv1, uv2 );
4398         // get nodes on seam and its vertices
4399         list< const SMDS_MeshNode* > seamNodes;
4400         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4401         while ( nSeamIt->more() ) {
4402           const SMDS_MeshNode* node = nSeamIt->next();
4403           if ( !isQuadratic || !IsMedium( node ))
4404             seamNodes.push_back( node );
4405         }
4406         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4407         for ( ; vExp.More(); vExp.Next() ) {
4408           sm = aMesh->MeshElements( vExp.Current() );
4409           if ( sm ) {
4410             nSeamIt = sm->GetNodes();
4411             while ( nSeamIt->more() )
4412               seamNodes.push_back( nSeamIt->next() );
4413           }
4414         }
4415         // loop on nodes on seam
4416         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4417         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4418           const SMDS_MeshNode* nSeam = *noSeIt;
4419           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4420           if ( n_uv == uvMap.end() )
4421             continue;
4422           // set the first UV
4423           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4424           // set the second UV
4425           listUV.push_back( *n_uv->second );
4426           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4427           if ( uvMap2.empty() )
4428             uvMap2 = uvMap; // copy the uvMap contents
4429           uvMap2[ nSeam ] = &listUV.back();
4430
4431           // collect movable nodes linked to ones on seam in nodesNearSeam
4432           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4433           while ( eIt->more() ) {
4434             const SMDS_MeshElement* e = eIt->next();
4435             int nbUseMap1 = 0, nbUseMap2 = 0;
4436             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4437             int nn = 0, nbn =  e->NbNodes();
4438             if(e->IsQuadratic()) nbn = nbn/2;
4439             while ( nn++ < nbn )
4440             {
4441               const SMDS_MeshNode* n =
4442                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4443               if (n == nSeam ||
4444                   setMovableNodes.find( n ) == setMovableNodes.end() )
4445                 continue;
4446               // add only nodes being closer to uv2 than to uv1
4447               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4448               //              0.5 * ( n->Y() + nSeam->Y() ),
4449               //              0.5 * ( n->Z() + nSeam->Z() ));
4450               // gp_XY uv;
4451               // getClosestUV( projector, pMid, uv );
4452               double x = uvMap[ n ]->Coord( iPar );
4453               if ( Abs( uv1.Coord( iPar ) - x ) >
4454                    Abs( uv2.Coord( iPar ) - x )) {
4455                 nodesNearSeam.insert( n );
4456                 nbUseMap2++;
4457               }
4458               else
4459                 nbUseMap1++;
4460             }
4461             // for centroidalSmooth all element nodes must
4462             // be on one side of a seam
4463             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4464               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4465               nn = 0;
4466               while ( nn++ < nbn ) {
4467                 const SMDS_MeshNode* n =
4468                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4469                 setMovableNodes.erase( n );
4470               }
4471             }
4472           }
4473         } // loop on nodes on seam
4474       } // loop on edge of a face
4475     } // if ( !face.IsNull() )
4476
4477     if ( setMovableNodes.empty() ) {
4478       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4479       continue; // goto next face
4480     }
4481
4482     // -------------
4483     // SMOOTHING //
4484     // -------------
4485
4486     int it = -1;
4487     double maxRatio = -1., maxDisplacement = -1.;
4488     set<const SMDS_MeshNode*>::iterator nodeToMove;
4489     for ( it = 0; it < theNbIterations; it++ ) {
4490       maxDisplacement = 0.;
4491       nodeToMove = setMovableNodes.begin();
4492       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4493         const SMDS_MeshNode* node = (*nodeToMove);
4494         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4495
4496         // smooth
4497         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4498         if ( theSmoothMethod == LAPLACIAN )
4499           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4500         else
4501           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4502
4503         // node displacement
4504         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4505         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4506         if ( aDispl > maxDisplacement )
4507           maxDisplacement = aDispl;
4508       }
4509       // no node movement => exit
4510       //if ( maxDisplacement < 1.e-16 ) {
4511       if ( maxDisplacement < disttol ) {
4512         MESSAGE("-- no node movement --");
4513         break;
4514       }
4515
4516       // check elements quality
4517       maxRatio  = 0;
4518       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4519       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4520         const SMDS_MeshElement* elem = (*elemIt);
4521         if ( !elem || elem->GetType() != SMDSAbs_Face )
4522           continue;
4523         SMESH::Controls::TSequenceOfXYZ aPoints;
4524         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4525           double aValue = aQualityFunc.GetValue( aPoints );
4526           if ( aValue > maxRatio )
4527             maxRatio = aValue;
4528         }
4529       }
4530       if ( maxRatio <= theTgtAspectRatio ) {
4531         //MESSAGE("-- quality achieved --");
4532         break;
4533       }
4534       if (it+1 == theNbIterations) {
4535         //MESSAGE("-- Iteration limit exceeded --");
4536       }
4537     } // smoothing iterations
4538
4539     // MESSAGE(" Face id: " << *fId <<
4540     //         " Nb iterstions: " << it <<
4541     //         " Displacement: " << maxDisplacement <<
4542     //         " Aspect Ratio " << maxRatio);
4543
4544     // ---------------------------------------
4545     // new nodes positions are computed,
4546     // record movement in DS and set new UV
4547     // ---------------------------------------
4548     nodeToMove = setMovableNodes.begin();
4549     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4550       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4551       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4552       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4553       if ( node_uv != uvMap.end() ) {
4554         gp_XY* uv = node_uv->second;
4555         node->SetPosition
4556           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4557       }
4558     }
4559
4560     // move medium nodes of quadratic elements
4561     if ( isQuadratic )
4562     {
4563       vector<const SMDS_MeshNode*> nodes;
4564       bool checkUV;
4565       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4566       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4567       {
4568         const SMDS_MeshElement* QF = *elemIt;
4569         if ( QF->IsQuadratic() )
4570         {
4571           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4572                         SMDS_MeshElement::iterator() );
4573           nodes.push_back( nodes[0] );
4574           gp_Pnt xyz;
4575           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4576           {
4577             if ( !surface.IsNull() )
4578             {
4579               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4580               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4581               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4582               xyz = surface->Value( uv.X(), uv.Y() );
4583             }
4584             else {
4585               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4586             }
4587             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4588               // we have to move a medium node
4589               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4590           }
4591         }
4592       }
4593     }
4594
4595   } // loop on face ids
4596
4597 }
4598
4599 namespace
4600 {
4601   //=======================================================================
4602   //function : isReverse
4603   //purpose  : Return true if normal of prevNodes is not co-directied with
4604   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4605   //           iNotSame is where prevNodes and nextNodes are different.
4606   //           If result is true then future volume orientation is OK
4607   //=======================================================================
4608
4609   bool isReverse(const SMDS_MeshElement*             face,
4610                  const vector<const SMDS_MeshNode*>& prevNodes,
4611                  const vector<const SMDS_MeshNode*>& nextNodes,
4612                  const int                           iNotSame)
4613   {
4614
4615     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4616     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4617     gp_XYZ extrDir( pN - pP ), faceNorm;
4618     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4619
4620     return faceNorm * extrDir < 0.0;
4621   }
4622
4623   //================================================================================
4624   /*!
4625    * \brief Assure that theElemSets[0] holds elements, not nodes
4626    */
4627   //================================================================================
4628
4629   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4630   {
4631     if ( !theElemSets[0].empty() &&
4632          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4633     {
4634       std::swap( theElemSets[0], theElemSets[1] );
4635     }
4636     else if ( !theElemSets[1].empty() &&
4637               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4638     {
4639       std::swap( theElemSets[0], theElemSets[1] );
4640     }
4641   }
4642 }
4643
4644 //=======================================================================
4645 /*!
4646  * \brief Create elements by sweeping an element
4647  * \param elem - element to sweep
4648  * \param newNodesItVec - nodes generated from each node of the element
4649  * \param newElems - generated elements
4650  * \param nbSteps - number of sweeping steps
4651  * \param srcElements - to append elem for each generated element
4652  */
4653 //=======================================================================
4654
4655 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4656                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4657                                     list<const SMDS_MeshElement*>&        newElems,
4658                                     const size_t                          nbSteps,
4659                                     SMESH_SequenceOfElemPtr&              srcElements)
4660 {
4661   SMESHDS_Mesh* aMesh = GetMeshDS();
4662
4663   const int           nbNodes = elem->NbNodes();
4664   const int         nbCorners = elem->NbCornerNodes();
4665   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4666                                                           polyhedron creation !!! */
4667   // Loop on elem nodes:
4668   // find new nodes and detect same nodes indices
4669   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4670   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4671   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4672   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4673
4674   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4675   vector<int> sames(nbNodes);
4676   vector<bool> isSingleNode(nbNodes);
4677
4678   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4679     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4680     const SMDS_MeshNode*                         node = nnIt->first;
4681     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4682     if ( listNewNodes.empty() )
4683       return;
4684
4685     itNN   [ iNode ] = listNewNodes.begin();
4686     prevNod[ iNode ] = node;
4687     nextNod[ iNode ] = listNewNodes.front();
4688
4689     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4690                                                              corner node of linear */
4691     if ( prevNod[ iNode ] != nextNod [ iNode ])
4692       nbDouble += !isSingleNode[iNode];
4693
4694     if( iNode < nbCorners ) { // check corners only
4695       if ( prevNod[ iNode ] == nextNod [ iNode ])
4696         sames[nbSame++] = iNode;
4697       else
4698         iNotSameNode = iNode;
4699     }
4700   }
4701
4702   if ( nbSame == nbNodes || nbSame > 2) {
4703     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4704     return;
4705   }
4706
4707   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4708   {
4709     // fix nodes order to have bottom normal external
4710     if ( baseType == SMDSEntity_Polygon )
4711     {
4712       std::reverse( itNN.begin(), itNN.end() );
4713       std::reverse( prevNod.begin(), prevNod.end() );
4714       std::reverse( midlNod.begin(), midlNod.end() );
4715       std::reverse( nextNod.begin(), nextNod.end() );
4716       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4717     }
4718     else
4719     {
4720       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4721       SMDS_MeshCell::applyInterlace( ind, itNN );
4722       SMDS_MeshCell::applyInterlace( ind, prevNod );
4723       SMDS_MeshCell::applyInterlace( ind, nextNod );
4724       SMDS_MeshCell::applyInterlace( ind, midlNod );
4725       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4726       if ( nbSame > 0 )
4727       {
4728         sames[nbSame] = iNotSameNode;
4729         for ( int j = 0; j <= nbSame; ++j )
4730           for ( size_t i = 0; i < ind.size(); ++i )
4731             if ( ind[i] == sames[j] )
4732             {
4733               sames[j] = i;
4734               break;
4735             }
4736         iNotSameNode = sames[nbSame];
4737       }
4738     }
4739   }
4740   else if ( elem->GetType() == SMDSAbs_Edge )
4741   {
4742     // orient a new face same as adjacent one
4743     int i1, i2;
4744     const SMDS_MeshElement* e;
4745     TIDSortedElemSet dummy;
4746     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4747         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4748         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4749     {
4750       // there is an adjacent face, check order of nodes in it
4751       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4752       if ( sameOrder )
4753       {
4754         std::swap( itNN[0],    itNN[1] );
4755         std::swap( prevNod[0], prevNod[1] );
4756         std::swap( nextNod[0], nextNod[1] );
4757         std::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4758         if ( nbSame > 0 )
4759           sames[0] = 1 - sames[0];
4760         iNotSameNode = 1 - iNotSameNode;
4761       }
4762     }
4763   }
4764
4765   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4766   if ( nbSame > 0 ) {
4767     iSameNode    = sames[ nbSame-1 ];
4768     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4769     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4770     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4771   }
4772
4773   if ( baseType == SMDSEntity_Polygon )
4774   {
4775     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4776     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4777   }
4778   else if ( baseType == SMDSEntity_Quad_Polygon )
4779   {
4780     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4781     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4782   }
4783
4784   // make new elements
4785   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4786   {
4787     // get next nodes
4788     for ( iNode = 0; iNode < nbNodes; iNode++ )
4789     {
4790       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4791       nextNod[ iNode ] = *itNN[ iNode ]++;
4792     }
4793
4794     SMDS_MeshElement* aNewElem = 0;
4795     /*if(!elem->IsPoly())*/ {
4796       switch ( baseType ) {
4797       case SMDSEntity_0D:
4798       case SMDSEntity_Node: { // sweep NODE
4799         if ( nbSame == 0 ) {
4800           if ( isSingleNode[0] )
4801             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4802           else
4803             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4804         }
4805         else
4806           return;
4807         break;
4808       }
4809       case SMDSEntity_Edge: { // sweep EDGE
4810         if ( nbDouble == 0 )
4811         {
4812           if ( nbSame == 0 ) // ---> quadrangle
4813             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4814                                       nextNod[ 1 ], nextNod[ 0 ] );
4815           else               // ---> triangle
4816             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4817                                       nextNod[ iNotSameNode ] );
4818         }
4819         else                 // ---> polygon
4820         {
4821           vector<const SMDS_MeshNode*> poly_nodes;
4822           poly_nodes.push_back( prevNod[0] );
4823           poly_nodes.push_back( prevNod[1] );
4824           if ( prevNod[1] != nextNod[1] )
4825           {
4826             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4827             poly_nodes.push_back( nextNod[1] );
4828           }
4829           if ( prevNod[0] != nextNod[0] )
4830           {
4831             poly_nodes.push_back( nextNod[0] );
4832             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4833           }
4834           switch ( poly_nodes.size() ) {
4835           case 3:
4836             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4837             break;
4838           case 4:
4839             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4840                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4841             break;
4842           default:
4843             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4844           }
4845         }
4846         break;
4847       }
4848       case SMDSEntity_Triangle: // TRIANGLE --->
4849       {
4850         if ( nbDouble > 0 ) break;
4851         if ( nbSame == 0 )       // ---> pentahedron
4852           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4853                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4854
4855         else if ( nbSame == 1 )  // ---> pyramid
4856           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4857                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4858                                        nextNod[ iSameNode ]);
4859
4860         else // 2 same nodes:       ---> tetrahedron
4861           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4862                                        nextNod[ iNotSameNode ]);
4863         break;
4864       }
4865       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4866       {
4867         if ( nbSame == 2 )
4868           return;
4869         if ( nbDouble+nbSame == 2 )
4870         {
4871           if(nbSame==0) {      // ---> quadratic quadrangle
4872             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4873                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4874           }
4875           else { //(nbSame==1) // ---> quadratic triangle
4876             if(sames[0]==2) {
4877               return; // medium node on axis
4878             }
4879             else if(sames[0]==0)
4880               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4881                                         prevNod[2], midlNod[1], nextNod[2] );
4882             else // sames[0]==1
4883               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4884                                         prevNod[2], nextNod[2], midlNod[0]);
4885           }
4886         }
4887         else if ( nbDouble == 3 )
4888         {
4889           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4890             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4891                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4892           }
4893         }
4894         else
4895           return;
4896         break;
4897       }
4898       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4899         if ( nbDouble > 0 ) break;
4900
4901         if ( nbSame == 0 )       // ---> hexahedron
4902           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4903                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4904
4905         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4906           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4907                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4908                                        nextNod[ iSameNode ]);
4909           newElems.push_back( aNewElem );
4910           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4911                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4912                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4913         }
4914         else if ( nbSame == 2 ) { // ---> pentahedron
4915           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4916             // iBeforeSame is same too
4917             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4918                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4919                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4920           else
4921             // iAfterSame is same too
4922             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4923                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4924                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4925         }
4926         break;
4927       }
4928       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4929       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4930         if ( nbDouble+nbSame != 3 ) break;
4931         if(nbSame==0) {
4932           // --->  pentahedron with 15 nodes
4933           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4934                                        nextNod[0], nextNod[1], nextNod[2],
4935                                        prevNod[3], prevNod[4], prevNod[5],
4936                                        nextNod[3], nextNod[4], nextNod[5],
4937                                        midlNod[0], midlNod[1], midlNod[2]);
4938         }
4939         else if(nbSame==1) {
4940           // --->  2d order pyramid of 13 nodes
4941           int apex = iSameNode;
4942           int i0 = ( apex + 1 ) % nbCorners;
4943           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4944           int i0a = apex + 3;
4945           int i1a = i1 + 3;
4946           int i01 = i0 + 3;
4947           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4948                                       nextNod[i0], nextNod[i1], prevNod[apex],
4949                                       prevNod[i01], midlNod[i0],
4950                                       nextNod[i01], midlNod[i1],
4951                                       prevNod[i1a], prevNod[i0a],
4952                                       nextNod[i0a], nextNod[i1a]);
4953         }
4954         else if(nbSame==2) {
4955           // --->  2d order tetrahedron of 10 nodes
4956           int n1 = iNotSameNode;
4957           int n2 = ( n1 + 1             ) % nbCorners;
4958           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4959           int n12 = n1 + 3;
4960           int n23 = n2 + 3;
4961           int n31 = n3 + 3;
4962           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4963                                        prevNod[n12], prevNod[n23], prevNod[n31],
4964                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4965         }
4966         break;
4967       }
4968       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4969         if( nbSame == 0 ) {
4970           if ( nbDouble != 4 ) break;
4971           // --->  hexahedron with 20 nodes
4972           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4973                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4974                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4975                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4976                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4977         }
4978         else if(nbSame==1) {
4979           // ---> pyramid + pentahedron - can not be created since it is needed
4980           // additional middle node at the center of face
4981           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4982           return;
4983         }
4984         else if( nbSame == 2 ) {
4985           if ( nbDouble != 2 ) break;
4986           // --->  2d order Pentahedron with 15 nodes
4987           int n1,n2,n4,n5;
4988           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4989             // iBeforeSame is same too
4990             n1 = iBeforeSame;
4991             n2 = iOpposSame;
4992             n4 = iSameNode;
4993             n5 = iAfterSame;
4994           }
4995           else {
4996             // iAfterSame is same too
4997             n1 = iSameNode;
4998             n2 = iBeforeSame;
4999             n4 = iAfterSame;
5000             n5 = iOpposSame;
5001           }
5002           int n12 = n2 + 4;
5003           int n45 = n4 + 4;
5004           int n14 = n1 + 4;
5005           int n25 = n5 + 4;
5006           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
5007                                        prevNod[n4], prevNod[n5], nextNod[n5],
5008                                        prevNod[n12], midlNod[n2], nextNod[n12],
5009                                        prevNod[n45], midlNod[n5], nextNod[n45],
5010                                        prevNod[n14], prevNod[n25], nextNod[n25]);
5011         }
5012         break;
5013       }
5014       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
5015
5016         if( nbSame == 0 && nbDouble == 9 ) {
5017           // --->  tri-quadratic hexahedron with 27 nodes
5018           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
5019                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
5020                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
5021                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
5022                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
5023                                        prevNod[8], // bottom center
5024                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
5025                                        nextNod[8], // top center
5026                                        midlNod[8]);// elem center
5027         }
5028         else
5029         {
5030           return;
5031         }
5032         break;
5033       }
5034       case SMDSEntity_Polygon: { // sweep POLYGON
5035
5036         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
5037           // --->  hexagonal prism
5038           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
5039                                        prevNod[3], prevNod[4], prevNod[5],
5040                                        nextNod[0], nextNod[1], nextNod[2],
5041                                        nextNod[3], nextNod[4], nextNod[5]);
5042         }
5043         break;
5044       }
5045       case SMDSEntity_Ball:
5046         return;
5047
5048       default:
5049         break;
5050       } // switch ( baseType )
5051     } // scope
5052
5053     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
5054     {
5055       if ( baseType != SMDSEntity_Polygon )
5056       {
5057         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
5058         SMDS_MeshCell::applyInterlace( ind, prevNod );
5059         SMDS_MeshCell::applyInterlace( ind, nextNod );
5060         SMDS_MeshCell::applyInterlace( ind, midlNod );
5061         SMDS_MeshCell::applyInterlace( ind, itNN );
5062         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
5063         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
5064       }
5065       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
5066       vector<int> quantities (nbNodes + 2);
5067       polyedre_nodes.clear();
5068       quantities.clear();
5069
5070       // bottom of prism
5071       for (int inode = 0; inode < nbNodes; inode++)
5072         polyedre_nodes.push_back( prevNod[inode] );
5073       quantities.push_back( nbNodes );
5074
5075       // top of prism
5076       polyedre_nodes.push_back( nextNod[0] );
5077       for (int inode = nbNodes; inode-1; --inode )
5078         polyedre_nodes.push_back( nextNod[inode-1] );
5079       quantities.push_back( nbNodes );
5080
5081       // side faces
5082       // 3--6--2
5083       // |     |
5084       // 7     5
5085       // |     |
5086       // 0--4--1
5087       const int iQuad = elem->IsQuadratic();
5088       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
5089       {
5090         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
5091         int inextface = (iface+1+iQuad) % nbNodes;
5092         int imid      = (iface+1) % nbNodes;
5093         polyedre_nodes.push_back( prevNod[inextface] );         // 0
5094         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
5095         polyedre_nodes.push_back( prevNod[iface] );             // 1
5096         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
5097         {
5098           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
5099           polyedre_nodes.push_back( nextNod[iface] );                         // 2
5100         }
5101         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
5102         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
5103         {
5104           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
5105           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
5106         }
5107         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
5108         if ( nbFaceNodes > 2 )
5109           quantities.push_back( nbFaceNodes );
5110         else // degenerated face
5111           polyedre_nodes.resize( prevNbNodes );
5112       }
5113       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
5114
5115     } // try to create a polyherdal prism
5116
5117     if ( aNewElem ) {
5118       newElems.push_back( aNewElem );
5119       myLastCreatedElems.push_back(aNewElem);
5120       srcElements.push_back( elem );
5121     }
5122
5123     // set new prev nodes
5124     for ( iNode = 0; iNode < nbNodes; iNode++ )
5125       prevNod[ iNode ] = nextNod[ iNode ];
5126
5127   } // loop on steps
5128 }
5129
5130 //=======================================================================
5131 /*!
5132  * \brief Create 1D and 2D elements around swept elements
5133  * \param mapNewNodes - source nodes and ones generated from them
5134  * \param newElemsMap - source elements and ones generated from them
5135  * \param elemNewNodesMap - nodes generated from each node of each element
5136  * \param elemSet - all swept elements
5137  * \param nbSteps - number of sweeping steps
5138  * \param srcElements - to append elem for each generated element
5139  */
5140 //=======================================================================
5141
5142 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
5143                                   TTElemOfElemListMap &    newElemsMap,
5144                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
5145                                   TIDSortedElemSet&        elemSet,
5146                                   const int                nbSteps,
5147                                   SMESH_SequenceOfElemPtr& srcElements)
5148 {
5149   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5150   SMESHDS_Mesh* aMesh = GetMeshDS();
5151
5152   // Find nodes belonging to only one initial element - sweep them into edges.
5153
5154   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5155   for ( ; nList != mapNewNodes.end(); nList++ )
5156   {
5157     const SMDS_MeshNode* node =
5158       static_cast<const SMDS_MeshNode*>( nList->first );
5159     if ( newElemsMap.count( node ))
5160       continue; // node was extruded into edge
5161     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5162     int nbInitElems = 0;
5163     const SMDS_MeshElement* el = 0;
5164     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5165     while ( eIt->more() && nbInitElems < 2 ) {
5166       const SMDS_MeshElement* e = eIt->next();
5167       SMDSAbs_ElementType  type = e->GetType();
5168       if ( type == SMDSAbs_Volume ||
5169            type < highType ||
5170            !elemSet.count(e))
5171         continue;
5172       if ( type > highType ) {
5173         nbInitElems = 0;
5174         highType    = type;
5175       }
5176       el = e;
5177       ++nbInitElems;
5178     }
5179     if ( nbInitElems == 1 ) {
5180       bool NotCreateEdge = el && el->IsMediumNode(node);
5181       if(!NotCreateEdge) {
5182         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5183         list<const SMDS_MeshElement*> newEdges;
5184         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5185       }
5186     }
5187   }
5188
5189   // Make a ceiling for each element ie an equal element of last new nodes.
5190   // Find free links of faces - make edges and sweep them into faces.
5191
5192   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5193
5194   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5195   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5196   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5197   {
5198     const SMDS_MeshElement* elem = itElem->first;
5199     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5200
5201     if(itElem->second.size()==0) continue;
5202
5203     const bool isQuadratic = elem->IsQuadratic();
5204
5205     if ( elem->GetType() == SMDSAbs_Edge ) {
5206       // create a ceiling edge
5207       if ( !isQuadratic ) {
5208         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5209                                vecNewNodes[ 1 ]->second.back())) {
5210           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5211                                                       vecNewNodes[ 1 ]->second.back()));
5212           srcElements.push_back( elem );
5213         }
5214       }
5215       else {
5216         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5217                                vecNewNodes[ 1 ]->second.back(),
5218                                vecNewNodes[ 2 ]->second.back())) {
5219           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5220                                                       vecNewNodes[ 1 ]->second.back(),
5221                                                       vecNewNodes[ 2 ]->second.back()));
5222           srcElements.push_back( elem );
5223         }
5224       }
5225     }
5226     if ( elem->GetType() != SMDSAbs_Face )
5227       continue;
5228
5229     bool hasFreeLinks = false;
5230
5231     TIDSortedElemSet avoidSet;
5232     avoidSet.insert( elem );
5233
5234     set<const SMDS_MeshNode*> aFaceLastNodes;
5235     int iNode, nbNodes = vecNewNodes.size();
5236     if ( !isQuadratic ) {
5237       // loop on the face nodes
5238       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5239         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5240         // look for free links of the face
5241         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5242         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5243         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5244         // check if a link n1-n2 is free
5245         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5246           hasFreeLinks = true;
5247           // make a new edge and a ceiling for a new edge
5248           const SMDS_MeshElement* edge;
5249           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5250             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5251             srcElements.push_back( myLastCreatedElems.back() );
5252           }
5253           n1 = vecNewNodes[ iNode ]->second.back();
5254           n2 = vecNewNodes[ iNext ]->second.back();
5255           if ( !aMesh->FindEdge( n1, n2 )) {
5256             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5257             srcElements.push_back( edge );
5258           }
5259         }
5260       }
5261     }
5262     else { // elem is quadratic face
5263       int nbn = nbNodes/2;
5264       for ( iNode = 0; iNode < nbn; iNode++ ) {
5265         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5266         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5267         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5268         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5269         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5270         // check if a link is free
5271         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5272              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5273              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5274           hasFreeLinks = true;
5275           // make an edge and a ceiling for a new edge
5276           // find medium node
5277           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5278             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5279             srcElements.push_back( elem );
5280           }
5281           n1 = vecNewNodes[ iNode ]->second.back();
5282           n2 = vecNewNodes[ iNext ]->second.back();
5283           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5284           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5285             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5286             srcElements.push_back( elem );
5287           }
5288         }
5289       }
5290       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5291         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5292       }
5293     }
5294
5295     // sweep free links into faces
5296
5297     if ( hasFreeLinks ) {
5298       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5299       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5300
5301       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5302       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5303       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5304         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5305         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5306       }
5307       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5308         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5309         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5310       }
5311       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5312         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5313         std::advance( v, volNb );
5314         // find indices of free faces of a volume and their source edges
5315         list< int > freeInd;
5316         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5317         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5318         int iF, nbF = vTool.NbFaces();
5319         for ( iF = 0; iF < nbF; iF ++ ) {
5320           if ( vTool.IsFreeFace( iF ) &&
5321                vTool.GetFaceNodes( iF, faceNodeSet ) &&
5322                initNodeSet != faceNodeSet) // except an initial face
5323           {
5324             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5325               continue;
5326             if ( faceNodeSet == initNodeSetNoCenter )
5327               continue;
5328             freeInd.push_back( iF );
5329             // find source edge of a free face iF
5330             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5331             vector<const SMDS_MeshNode*>::iterator lastCommom;
5332             commonNodes.resize( nbNodes, 0 );
5333             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5334                                                 initNodeSet.begin(), initNodeSet.end(),
5335                                                 commonNodes.begin());
5336             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5337               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5338             else
5339               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5340
5341             if (SALOME::VerbosityActivated() && !srcEdges.back())
5342             {
5343               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5344                   << iF << " of volume #" << vTool.ID() << endl;
5345             }
5346           }
5347         }
5348         if ( freeInd.empty() )
5349           continue;
5350
5351         // create wall faces for all steps;
5352         // if such a face has been already created by sweep of edge,
5353         // assure that its orientation is OK
5354         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5355         {
5356           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5357           vTool.SetExternalNormal();
5358           const int nextShift = vTool.IsForward() ? +1 : -1;
5359           list< int >::iterator ind = freeInd.begin();
5360           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5361           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5362           {
5363             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5364             int nbn = vTool.NbFaceNodes( *ind );
5365             const SMDS_MeshElement * f = 0;
5366             if ( nbn == 3 )              ///// triangle
5367             {
5368               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5369               if ( !f ||
5370                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5371               {
5372                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5373                                                      nodes[ 1 ],
5374                                                      nodes[ 1 + nextShift ] };
5375                 if ( f )
5376                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5377                 else
5378                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5379                                                                newOrder[ 2 ] ));
5380               }
5381             }
5382             else if ( nbn == 4 )       ///// quadrangle
5383             {
5384               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5385               if ( !f ||
5386                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5387               {
5388                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5389                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5390                 if ( f )
5391                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5392                 else
5393                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5394                                                                newOrder[ 2 ], newOrder[ 3 ]));
5395               }
5396             }
5397             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5398             {
5399               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5400               if ( !f ||
5401                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5402               {
5403                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5404                                                      nodes[2],
5405                                                      nodes[2 + 2*nextShift],
5406                                                      nodes[3 - 2*nextShift],
5407                                                      nodes[3],
5408                                                      nodes[3 + 2*nextShift]};
5409                 if ( f )
5410                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5411                 else
5412                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5413                                                                newOrder[ 1 ],
5414                                                                newOrder[ 2 ],
5415                                                                newOrder[ 3 ],
5416                                                                newOrder[ 4 ],
5417                                                                newOrder[ 5 ] ));
5418               }
5419             }
5420             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5421             {
5422               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5423                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5424               if ( !f ||
5425                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5426               {
5427                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5428                                                      nodes[4 - 2*nextShift],
5429                                                      nodes[4],
5430                                                      nodes[4 + 2*nextShift],
5431                                                      nodes[1],
5432                                                      nodes[5 - 2*nextShift],
5433                                                      nodes[5],
5434                                                      nodes[5 + 2*nextShift] };
5435                 if ( f )
5436                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5437                 else
5438                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5439                                                               newOrder[ 2 ], newOrder[ 3 ],
5440                                                               newOrder[ 4 ], newOrder[ 5 ],
5441                                                               newOrder[ 6 ], newOrder[ 7 ]));
5442               }
5443             }
5444             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5445             {
5446               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5447                                       SMDSAbs_Face, /*noMedium=*/false);
5448               if ( !f ||
5449                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5450               {
5451                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5452                                                      nodes[4 - 2*nextShift],
5453                                                      nodes[4],
5454                                                      nodes[4 + 2*nextShift],
5455                                                      nodes[1],
5456                                                      nodes[5 - 2*nextShift],
5457                                                      nodes[5],
5458                                                      nodes[5 + 2*nextShift],
5459                                                      nodes[8] };
5460                 if ( f )
5461                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5462                 else
5463                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5464                                                               newOrder[ 2 ], newOrder[ 3 ],
5465                                                               newOrder[ 4 ], newOrder[ 5 ],
5466                                                               newOrder[ 6 ], newOrder[ 7 ],
5467                                                               newOrder[ 8 ]));
5468               }
5469             }
5470             else  //////// polygon
5471             {
5472               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5473               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5474               if ( !f ||
5475                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5476               {
5477                 if ( !vTool.IsForward() )
5478                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5479                 if ( f )
5480                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5481                 else
5482                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5483               }
5484             }
5485
5486             while ( srcElements.size() < myLastCreatedElems.size() )
5487               srcElements.push_back( *srcEdge );
5488
5489           }  // loop on free faces
5490
5491           // go to the next volume
5492           iVol = 0;
5493           while ( iVol++ < nbVolumesByStep ) v++;
5494
5495         } // loop on steps
5496       } // loop on volumes of one step
5497     } // sweep free links into faces
5498
5499     // Make a ceiling face with a normal external to a volume
5500
5501     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5502     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5503     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5504
5505     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5506       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5507       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5508     }
5509     if ( iF >= 0 )
5510     {
5511       lastVol.SetExternalNormal();
5512       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5513       const               int nbn = lastVol.NbFaceNodes( iF );
5514       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5515       if ( !hasFreeLinks ||
5516            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5517       {
5518         const vector<int>& interlace =
5519           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5520         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5521
5522         AddElement( nodeVec, anyFace.Init( elem ));
5523
5524         while ( srcElements.size() < myLastCreatedElems.size() )
5525           srcElements.push_back( elem );
5526       }
5527     }
5528   } // loop on swept elements
5529 }
5530
5531 //=======================================================================
5532 //function : RotationSweep
5533 //purpose  :
5534 //=======================================================================
5535
5536 SMESH_MeshEditor::PGroupIDs
5537 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5538                                 const gp_Ax1&      theAxis,
5539                                 const double       theAngle,
5540                                 const int          theNbSteps,
5541                                 const double       theTol,
5542                                 const bool         theMakeGroups,
5543                                 const bool         theMakeWalls)
5544 {
5545   ClearLastCreated();
5546
5547   setElemsFirst( theElemSets );
5548   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5549   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5550
5551   // source elements for each generated one
5552   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5553   srcElems.reserve( theElemSets[0].size() );
5554   srcNodes.reserve( theElemSets[1].size() );
5555
5556   gp_Trsf aTrsf;
5557   aTrsf.SetRotation( theAxis, theAngle );
5558   gp_Trsf aTrsf2;
5559   aTrsf2.SetRotation( theAxis, theAngle/2. );
5560
5561   gp_Lin aLine( theAxis );
5562   double aSqTol = theTol * theTol;
5563
5564   SMESHDS_Mesh* aMesh = GetMeshDS();
5565
5566   TNodeOfNodeListMap mapNewNodes;
5567   TElemOfVecOfNnlmiMap mapElemNewNodes;
5568   TTElemOfElemListMap newElemsMap;
5569
5570   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5571                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5572                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5573   // loop on theElemSets
5574   TIDSortedElemSet::iterator itElem;
5575   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5576   {
5577     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5578     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5579       const SMDS_MeshElement* elem = *itElem;
5580       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5581         continue;
5582       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5583       newNodesItVec.reserve( elem->NbNodes() );
5584
5585       // loop on elem nodes
5586       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5587       while ( itN->more() )
5588       {
5589         const SMDS_MeshNode* node = cast2Node( itN->next() );
5590
5591         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5592         double coord[3];
5593         aXYZ.Coord( coord[0], coord[1], coord[2] );
5594         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5595
5596         // check if a node has been already sweeped
5597         TNodeOfNodeListMapItr nIt =
5598           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5599         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5600         if ( listNewNodes.empty() )
5601         {
5602           // check if we are to create medium nodes between corner ones
5603           bool needMediumNodes = false;
5604           if ( isQuadraticMesh )
5605           {
5606             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5607             while (it->more() && !needMediumNodes )
5608             {
5609               const SMDS_MeshElement* invElem = it->next();
5610               if ( invElem != elem && !theElems.count( invElem )) continue;
5611               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5612               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5613                 needMediumNodes = true;
5614             }
5615           }
5616
5617           // make new nodes
5618           const SMDS_MeshNode * newNode = node;
5619           for ( int i = 0; i < theNbSteps; i++ ) {
5620             if ( !isOnAxis ) {
5621               if ( needMediumNodes )  // create a medium node
5622               {
5623                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5624                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5625                 myLastCreatedNodes.push_back(newNode);
5626                 srcNodes.push_back( node );
5627                 listNewNodes.push_back( newNode );
5628                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5629               }
5630               else {
5631                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5632               }
5633               // create a corner node
5634               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5635               myLastCreatedNodes.push_back(newNode);
5636               srcNodes.push_back( node );
5637               listNewNodes.push_back( newNode );
5638             }
5639             else {
5640               listNewNodes.push_back( newNode );
5641               // if ( needMediumNodes )
5642               //   listNewNodes.push_back( newNode );
5643             }
5644           }
5645         }
5646         newNodesItVec.push_back( nIt );
5647       }
5648       // make new elements
5649       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5650     }
5651   }
5652
5653   if ( theMakeWalls )
5654     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5655
5656   PGroupIDs newGroupIDs;
5657   if ( theMakeGroups )
5658     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5659
5660   return newGroupIDs;
5661 }
5662
5663 //=======================================================================
5664 //function : ExtrusParam
5665 //purpose  : standard construction
5666 //=======================================================================
5667
5668 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5669                                             const int                theNbSteps,
5670                                             const std::list<double>& theScales,
5671                                             const std::list<double>& theAngles,
5672                                             const gp_XYZ*            theBasePoint,
5673                                             const int                theFlags,
5674                                             const double             theTolerance):
5675   myDir( theStep ),
5676   myBaseP( Precision::Infinite(), 0, 0 ),
5677   myFlags( theFlags ),
5678   myTolerance( theTolerance ),
5679   myElemsToUse( NULL )
5680 {
5681   mySteps = new TColStd_HSequenceOfReal;
5682   const double stepSize = theStep.Magnitude();
5683   for (int i=1; i<=theNbSteps; i++ )
5684     mySteps->Append( stepSize );
5685
5686   if ( !theScales.empty() )
5687   {
5688     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5689       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5690
5691     // add medium scales
5692     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5693     myScales.reserve( theNbSteps * 2 );
5694     myScales.push_back( 0.5 * ( *s1 + 1. ));
5695     myScales.push_back( *s1 );
5696     for ( ; s2 != theScales.end(); s1 = s2++ )
5697     {
5698       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5699       myScales.push_back( *s2 );
5700     }
5701   }
5702
5703   if ( !theAngles.empty() )
5704   {
5705     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5706     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5707       linearAngleVariation( theNbSteps, angles );
5708
5709     // accumulate angles
5710     double angle = 0;
5711     int nbAngles = 0;
5712     std::list<double>::iterator a1 = angles.begin(), a2;
5713     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5714     {
5715       angle += *a1;
5716       *a1 = angle;
5717     }
5718     while ( nbAngles++ < theNbSteps )
5719       angles.push_back( angles.back() );
5720
5721     // add medium angles
5722     a2 = angles.begin(), a1 = a2++;
5723     myAngles.push_back( 0.5 * *a1 );
5724     myAngles.push_back( *a1 );
5725     for ( ; a2 != angles.end(); a1 = a2++ )
5726     {
5727       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5728       myAngles.push_back( *a2 );
5729     }
5730   }
5731
5732   if ( theBasePoint )
5733   {
5734     myBaseP = *theBasePoint;
5735   }
5736
5737   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5738       ( theTolerance > 0 ))
5739   {
5740     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5741   }
5742   else
5743   {
5744     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5745   }
5746 }
5747
5748 //=======================================================================
5749 //function : ExtrusParam
5750 //purpose  : steps are given explicitly
5751 //=======================================================================
5752
5753 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5754                                             Handle(TColStd_HSequenceOfReal) theSteps,
5755                                             const int                       theFlags,
5756                                             const double                    theTolerance):
5757   myDir( theDir ),
5758   mySteps( theSteps ),
5759   myFlags( theFlags ),
5760   myTolerance( theTolerance ),
5761   myElemsToUse( NULL )
5762 {
5763   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5764       ( theTolerance > 0 ))
5765   {
5766     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5767   }
5768   else
5769   {
5770     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5771   }
5772 }
5773
5774 //=======================================================================
5775 //function : ExtrusParam
5776 //purpose  : for extrusion by normal
5777 //=======================================================================
5778
5779 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5780                                             const int    theNbSteps,
5781                                             const int    theFlags,
5782                                             const int    theDim ):
5783   myDir( 1,0,0 ),
5784   mySteps( new TColStd_HSequenceOfReal ),
5785   myFlags( theFlags ),
5786   myTolerance( 0 ),
5787   myElemsToUse( NULL )
5788 {
5789   for (int i = 0; i < theNbSteps; i++ )
5790     mySteps->Append( theStepSize );
5791
5792   if ( theDim == 1 )
5793   {
5794     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5795   }
5796   else
5797   {
5798     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5799   }
5800 }
5801
5802 //=======================================================================
5803 //function : ExtrusParam
5804 //purpose  : for extrusion along path
5805 //=======================================================================
5806
5807 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5808                                             const gp_Pnt*                   theBasePoint,
5809                                             const std::list<double>&        theScales,
5810                                             const bool                      theMakeGroups )
5811   : myBaseP( Precision::Infinite(), 0, 0 ),
5812     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5813     myPathPoints( thePoints )
5814 {
5815   if ( theBasePoint )
5816   {
5817     myBaseP = theBasePoint->XYZ();
5818   }
5819
5820   if ( !theScales.empty() )
5821   {
5822     // add medium scales
5823     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5824     myScales.reserve( thePoints.size() * 2 );
5825     myScales.push_back( 0.5 * ( 1. + *s1 ));
5826     myScales.push_back( *s1 );
5827     for ( ; s2 != theScales.end(); s1 = s2++ )
5828     {
5829       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5830       myScales.push_back( *s2 );
5831     }
5832   }
5833
5834   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5835 }
5836
5837 //=======================================================================
5838 //function : ExtrusParam::SetElementsToUse
5839 //purpose  : stores elements to use for extrusion by normal, depending on
5840 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5841 //           define myBaseP for scaling
5842 //=======================================================================
5843
5844 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5845                                                       const TIDSortedElemSet& nodes )
5846 {
5847   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5848
5849   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5850   {
5851     myBaseP.SetCoord( 0.,0.,0. );
5852     TIDSortedElemSet newNodes;
5853
5854     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5855     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5856     {
5857       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5858       TIDSortedElemSet::const_iterator itElem = elements.begin();
5859       for ( ; itElem != elements.end(); itElem++ )
5860       {
5861         const SMDS_MeshElement* elem = *itElem;
5862         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5863         while ( itN->more() ) {
5864           const SMDS_MeshElement* node = itN->next();
5865           if ( newNodes.insert( node ).second )
5866             myBaseP += SMESH_NodeXYZ( node );
5867         }
5868       }
5869     }
5870     myBaseP /= newNodes.size();
5871   }
5872 }
5873
5874 //=======================================================================
5875 //function : ExtrusParam::beginStepIter
5876 //purpose  : prepare iteration on steps
5877 //=======================================================================
5878
5879 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5880 {
5881   myWithMediumNodes = withMediumNodes;
5882   myNextStep = 1;
5883   myCurSteps.clear();
5884 }
5885 //=======================================================================
5886 //function : ExtrusParam::moreSteps
5887 //purpose  : are there more steps?
5888 //=======================================================================
5889
5890 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5891 {
5892   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5893 }
5894 //=======================================================================
5895 //function : ExtrusParam::nextStep
5896 //purpose  : returns the next step
5897 //=======================================================================
5898
5899 double SMESH_MeshEditor::ExtrusParam::nextStep()
5900 {
5901   double res = 0;
5902   if ( !myCurSteps.empty() )
5903   {
5904     res = myCurSteps.back();
5905     myCurSteps.pop_back();
5906   }
5907   else if ( myNextStep <= mySteps->Length() )
5908   {
5909     myCurSteps.push_back( mySteps->Value( myNextStep ));
5910     ++myNextStep;
5911     if ( myWithMediumNodes )
5912     {
5913       myCurSteps.back() /= 2.;
5914       myCurSteps.push_back( myCurSteps.back() );
5915     }
5916     res = nextStep();
5917   }
5918   return res;
5919 }
5920
5921 //=======================================================================
5922 //function : ExtrusParam::makeNodesByDir
5923 //purpose  : create nodes for standard extrusion
5924 //=======================================================================
5925
5926 int SMESH_MeshEditor::ExtrusParam::
5927 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5928                 const SMDS_MeshNode*              srcNode,
5929                 std::list<const SMDS_MeshNode*> & newNodes,
5930                 const bool                        makeMediumNodes)
5931 {
5932   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5933
5934   int nbNodes = 0;
5935   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5936   {
5937     p += myDir.XYZ() * nextStep();
5938     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5939     newNodes.push_back( newNode );
5940   }
5941
5942   if ( !myScales.empty() || !myAngles.empty() )
5943   {
5944     gp_XYZ  center = myBaseP;
5945     gp_Ax1  ratationAxis( center, myDir );
5946     gp_Trsf rotation;
5947
5948     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5949     size_t i = !makeMediumNodes;
5950     for ( beginStepIter( makeMediumNodes );
5951           moreSteps();
5952           ++nIt, i += 1 + !makeMediumNodes )
5953     {
5954       center += myDir.XYZ() * nextStep();
5955
5956       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5957       bool moved = false;
5958       if ( i < myScales.size() )
5959       {
5960         xyz = ( myScales[i] * ( xyz - center )) + center;
5961         moved = true;
5962       }
5963       if ( !myAngles.empty() )
5964       {
5965         rotation.SetRotation( ratationAxis, myAngles[i] );
5966         rotation.Transforms( xyz );
5967         moved = true;
5968       }
5969       if ( moved )
5970         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5971       else
5972         break;
5973     }
5974   }
5975   return nbNodes;
5976 }
5977
5978 //=======================================================================
5979 //function : ExtrusParam::makeNodesByDirAndSew
5980 //purpose  : create nodes for standard extrusion with sewing
5981 //=======================================================================
5982
5983 int SMESH_MeshEditor::ExtrusParam::
5984 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5985                       const SMDS_MeshNode*              srcNode,
5986                       std::list<const SMDS_MeshNode*> & newNodes,
5987                       const bool                        makeMediumNodes)
5988 {
5989   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5990
5991   int nbNodes = 0;
5992   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5993   {
5994     P1 += myDir.XYZ() * nextStep();
5995
5996     // try to search in sequence of existing nodes
5997     // if myNodes.size()>0 we 'nave to use given sequence
5998     // else - use all nodes of mesh
5999     const SMDS_MeshNode * node = 0;
6000     if ( myNodes.Length() > 0 )
6001     {
6002       for ( int i = 1; i <= myNodes.Length(); i++ )
6003       {
6004         SMESH_NodeXYZ P2 = myNodes.Value(i);
6005         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6006         {
6007           node = myNodes.Value(i);
6008           break;
6009         }
6010       }
6011     }
6012     else
6013     {
6014       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
6015       while(itn->more())
6016       {
6017         SMESH_NodeXYZ P2 = itn->next();
6018         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6019         {
6020           node = P2._node;
6021           break;
6022         }
6023       }
6024     }
6025
6026     if ( !node )
6027       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
6028
6029     newNodes.push_back( node );
6030
6031   } // loop on steps
6032
6033   return nbNodes;
6034 }
6035
6036 //=======================================================================
6037 //function : ExtrusParam::makeNodesByNormal2D
6038 //purpose  : create nodes for extrusion using normals of faces
6039 //=======================================================================
6040
6041 int SMESH_MeshEditor::ExtrusParam::
6042 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
6043                      const SMDS_MeshNode*              srcNode,
6044                      std::list<const SMDS_MeshNode*> & newNodes,
6045                      const bool                        makeMediumNodes)
6046 {
6047   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
6048
6049   gp_XYZ p = SMESH_NodeXYZ( srcNode );
6050
6051   // get normals to faces sharing srcNode
6052   vector< gp_XYZ > norms, baryCenters;
6053   gp_XYZ norm, avgNorm( 0,0,0 );
6054   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
6055   while ( faceIt->more() )
6056   {
6057     const SMDS_MeshElement* face = faceIt->next();
6058     if ( myElemsToUse && !myElemsToUse->count( face ))
6059       continue;
6060     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
6061     {
6062       norms.push_back( norm );
6063       avgNorm += norm;
6064       if ( !alongAvgNorm )
6065       {
6066         gp_XYZ bc(0,0,0);
6067         int nbN = 0;
6068         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
6069           bc += SMESH_NodeXYZ( nIt->next() );
6070         baryCenters.push_back( bc / nbN );
6071       }
6072     }
6073   }
6074
6075   if ( norms.empty() ) return 0;
6076
6077   double normSize = avgNorm.Modulus();
6078   if ( normSize < std::numeric_limits<double>::min() )
6079     return 0;
6080
6081   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
6082   {
6083     myDir = avgNorm;
6084     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
6085   }
6086
6087   avgNorm /= normSize;
6088
6089   int nbNodes = 0;
6090   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
6091   {
6092     gp_XYZ pNew = p;
6093     double stepSize = nextStep();
6094
6095     if ( norms.size() > 1 )
6096     {
6097       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
6098       {
6099         // translate plane of a face
6100         baryCenters[ iF ] += norms[ iF ] * stepSize;
6101
6102         // find point of intersection of the face plane located at baryCenters[ iF ]
6103         // and avgNorm located at pNew
6104         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
6105         double dot  = ( norms[ iF ] * avgNorm );
6106         if ( dot < std::numeric_limits<double>::min() )
6107           dot = stepSize * 1e-3;
6108         double step = -( norms[ iF ] * pNew + d ) / dot;
6109         pNew += step * avgNorm;
6110       }
6111     }
6112     else
6113     {
6114       pNew += stepSize * avgNorm;
6115     }
6116     p = pNew;
6117
6118     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6119     newNodes.push_back( newNode );
6120   }
6121   return nbNodes;
6122 }
6123
6124 //=======================================================================
6125 //function : ExtrusParam::makeNodesByNormal1D
6126 //purpose  : create nodes for extrusion using normals of edges
6127 //=======================================================================
6128
6129 int SMESH_MeshEditor::ExtrusParam::
6130 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
6131                      const SMDS_MeshNode*              /*srcNode*/,
6132                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
6133                      const bool                        /*makeMediumNodes*/)
6134 {
6135   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6136   return 0;
6137 }
6138
6139 //=======================================================================
6140 //function : ExtrusParam::makeNodesAlongTrack
6141 //purpose  : create nodes for extrusion along path
6142 //=======================================================================
6143
6144 int SMESH_MeshEditor::ExtrusParam::
6145 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
6146                      const SMDS_MeshNode*              srcNode,
6147                      std::list<const SMDS_MeshNode*> & newNodes,
6148                      const bool                        makeMediumNodes)
6149 {
6150   const Standard_Real aTolAng=1.e-4;
6151
6152   gp_Pnt aV0x = myBaseP;
6153   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6154
6155   const PathPoint& aPP0 = myPathPoints[0];
6156   gp_Pnt aP0x = aPP0.myPnt;
6157   gp_Dir aDT0x= aPP0.myTgt;
6158
6159   std::vector< gp_Pnt > centers;
6160   centers.reserve( NbSteps() * 2 );
6161
6162   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6163
6164   for ( size_t j = 1; j < myPathPoints.size(); ++j )
6165   {
6166     const PathPoint&  aPP  = myPathPoints[j];
6167     const gp_Pnt&     aP1x = aPP.myPnt;
6168     const gp_Dir&    aDT1x = aPP.myTgt;
6169
6170     // Translation
6171     gp_Vec aV01x( aP0x, aP1x );
6172     aTrsf.SetTranslation( aV01x );
6173     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6174     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6175
6176     // rotation 1 [ T1,T0 ]
6177     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6178     if ( fabs( aAngleT1T0 ) > aTolAng )
6179     {
6180       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6181       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6182
6183       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6184     }
6185
6186     // rotation 2
6187     if ( aPP.myAngle != 0. )
6188     {
6189       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6190       aPN1 = aPN1.Transformed( aTrsfRot );
6191     }
6192
6193     // make new node
6194     if ( makeMediumNodes )
6195     {
6196       // create additional node
6197       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6198       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6199       newNodes.push_back( newNode );
6200
6201     }
6202     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6203     newNodes.push_back( newNode );
6204
6205     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6206     centers.push_back( aV1x );
6207
6208     aPN0 = aPN1;
6209     aP0x = aP1x;
6210     aV0x = aV1x;
6211     aDT0x = aDT1x;
6212   }
6213
6214   // scale
6215   if ( !myScales.empty() )
6216   {
6217     gp_Trsf aTrsfScale;
6218     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6219     for ( size_t i = !makeMediumNodes;
6220           i < myScales.size() && node != newNodes.end();
6221           i += ( 1 + !makeMediumNodes ), ++node )
6222     {
6223       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6224       gp_Pnt aN = SMESH_NodeXYZ( *node );
6225       gp_Pnt aP = aN.Transformed( aTrsfScale );
6226       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6227     }
6228   }
6229
6230   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6231 }
6232
6233 //=======================================================================
6234 //function : ExtrusionSweep
6235 //purpose  :
6236 //=======================================================================
6237
6238 SMESH_MeshEditor::PGroupIDs
6239 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
6240                                   const gp_Vec&        theStep,
6241                                   const int            theNbSteps,
6242                                   TTElemOfElemListMap& newElemsMap,
6243                                   const int            theFlags,
6244                                   const double         theTolerance)
6245 {
6246   std::list<double> dummy;
6247   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6248                        theFlags, theTolerance );
6249   return ExtrusionSweep( theElems, aParams, newElemsMap );
6250 }
6251
6252 namespace
6253 {
6254
6255 //=======================================================================
6256 //function : getOriFactor
6257 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
6258 //           edge curve orientation
6259 //=======================================================================
6260
6261   double getOriFactor( const TopoDS_Edge&   edge,
6262                        const SMDS_MeshNode* n1,
6263                        const SMDS_MeshNode* n2,
6264                        SMESH_MesherHelper&  helper)
6265   {
6266     double u1 = helper.GetNodeU( edge, n1, n2 );
6267     double u2 = helper.GetNodeU( edge, n2, n1 );
6268     return u1 < u2 ? 1. : -1.;
6269   }
6270 }
6271
6272 //=======================================================================
6273 //function : ExtrusionSweep
6274 //purpose  :
6275 //=======================================================================
6276
6277 SMESH_MeshEditor::PGroupIDs
6278 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
6279                                   ExtrusParam&         theParams,
6280                                   TTElemOfElemListMap& newElemsMap)
6281 {
6282   ClearLastCreated();
6283
6284   setElemsFirst( theElemSets );
6285   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6286   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6287
6288   // source elements for each generated one
6289   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6290   srcElems.reserve( theElemSets[0].size() );
6291   srcNodes.reserve( theElemSets[1].size() );
6292
6293   const int nbSteps = theParams.NbSteps();
6294   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6295
6296   TNodeOfNodeListMap   mapNewNodes;
6297   TElemOfVecOfNnlmiMap mapElemNewNodes;
6298
6299   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6300                                      myMesh->NbFaces(ORDER_QUADRATIC) +
6301                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
6302   // loop on theElems
6303   TIDSortedElemSet::iterator itElem;
6304   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6305   {
6306     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6307     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6308     {
6309       // check element type
6310       const SMDS_MeshElement* elem = *itElem;
6311       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
6312         continue;
6313
6314       const size_t nbNodes = elem->NbNodes();
6315       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6316       newNodesItVec.reserve( nbNodes );
6317
6318       // loop on elem nodes
6319       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6320       while ( itN->more() )
6321       {
6322         // check if a node has been already sweeped
6323         const SMDS_MeshNode* node = itN->next();
6324         TNodeOfNodeListMap::iterator nIt =
6325           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6326         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6327         if ( listNewNodes.empty() )
6328         {
6329           // make new nodes
6330
6331           // check if we are to create medium nodes between corner ones
6332           bool needMediumNodes = false;
6333           if ( isQuadraticMesh )
6334           {
6335             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6336             while (it->more() && !needMediumNodes )
6337             {
6338               const SMDS_MeshElement* invElem = it->next();
6339               if ( invElem != elem && !theElems.count( invElem )) continue;
6340               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6341               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6342                 needMediumNodes = true;
6343             }
6344           }
6345           // create nodes for all steps
6346           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6347           {
6348             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6349             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6350             {
6351               myLastCreatedNodes.push_back( *newNodesIt );
6352               srcNodes.push_back( node );
6353             }
6354           }
6355           else
6356           {
6357             if ( theParams.ToMakeBoundary() )
6358             {
6359               GetMeshDS()->Modified();
6360               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6361             }
6362             break; // newNodesItVec will be shorter than nbNodes
6363           }
6364         }
6365         newNodesItVec.push_back( nIt );
6366       }
6367       // make new elements
6368       if ( newNodesItVec.size() == nbNodes )
6369         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6370     }
6371   }
6372
6373   if ( theParams.ToMakeBoundary() ) {
6374     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6375   }
6376   PGroupIDs newGroupIDs;
6377   if ( theParams.ToMakeGroups() )
6378     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6379
6380   return newGroupIDs;
6381 }
6382
6383 //=======================================================================
6384 //function : ExtrusionAlongTrack
6385 //purpose  :
6386 //=======================================================================
6387 SMESH_MeshEditor::Extrusion_Error
6388 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6389                                        SMESH_Mesh*          theTrackMesh,
6390                                        SMDS_ElemIteratorPtr theTrackIterator,
6391                                        const SMDS_MeshNode* theN1,
6392                                        std::list<double>&   theAngles,
6393                                        const bool           theAngleVariation,
6394                                        std::list<double>&   theScales,
6395                                        const bool           theScaleVariation,
6396                                        const gp_Pnt*        theRefPoint,
6397                                        const bool           theMakeGroups)
6398 {
6399   ClearLastCreated();
6400
6401   // 1. Check data
6402   if ( theElements[0].empty() && theElements[1].empty() )
6403     return EXTR_NO_ELEMENTS;
6404
6405   ASSERT( theTrackMesh );
6406   if ( ! theTrackIterator || !theTrackIterator->more() )
6407     return EXTR_NO_ELEMENTS;
6408
6409   // 2. Get ordered nodes
6410   SMESH_MeshAlgos::TElemGroupVector branchEdges;
6411   SMESH_MeshAlgos::TNodeGroupVector branchNods;
6412   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6413   if ( branchEdges.empty() )
6414     return EXTR_PATH_NOT_EDGE;
6415
6416   if ( branchEdges.size() > 1 )
6417     return EXTR_BAD_PATH_SHAPE;
6418
6419   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
6420   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6421   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6422     return EXTR_BAD_STARTING_NODE;
6423
6424   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6425   {
6426     // add medium nodes to pathNodes
6427     std::vector< const SMDS_MeshNode* >    pathNodes2;
6428     std::vector< const SMDS_MeshElement* > pathEdges2;
6429     pathNodes2.reserve( pathNodes.size() * 2 );
6430     pathEdges2.reserve( pathEdges.size() * 2 );
6431     for ( size_t i = 0; i < pathEdges.size(); ++i )
6432     {
6433       pathNodes2.push_back( pathNodes[i] );
6434       pathEdges2.push_back( pathEdges[i] );
6435       if ( pathEdges[i]->IsQuadratic() )
6436       {
6437         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6438         pathEdges2.push_back( pathEdges[i] );
6439       }
6440     }
6441     pathNodes2.push_back( pathNodes.back() );
6442     pathEdges.swap( pathEdges2 );
6443     pathNodes.swap( pathNodes2 );
6444   }
6445
6446   // 3. Get path data at pathNodes
6447
6448   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6449
6450   if ( theAngleVariation )
6451     linearAngleVariation( points.size()-1, theAngles );
6452   if ( theScaleVariation )
6453     linearScaleVariation( points.size()-1, theScales );
6454
6455   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6456   std::list<double>::iterator angle = theAngles.begin();
6457
6458   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6459
6460   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6461   std::map< int, double >::iterator id2factor;
6462   SMESH_MesherHelper pathHelper( *theTrackMesh );
6463   gp_Pnt p; gp_Vec tangent;
6464   const double tol2 = gp::Resolution() * gp::Resolution();
6465
6466   for ( size_t i = 0; i < pathNodes.size(); ++i )
6467   {
6468     ExtrusParam::PathPoint & point = points[ i ];
6469
6470     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6471
6472     if ( angle != theAngles.end() )
6473       point.myAngle = *angle++;
6474
6475     tangent.SetCoord( 0,0,0 );
6476     const int          shapeID = pathNodes[ i ]->GetShapeID();
6477     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6478     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6479     switch ( shapeType )
6480     {
6481     case TopAbs_EDGE:
6482     {
6483       TopoDS_Edge edge = TopoDS::Edge( shape );
6484       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6485       if ( id2factor->second == 0 )
6486       {
6487         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6488         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6489       }
6490       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6491       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6492       curve->D1( u, p, tangent );
6493       tangent *= id2factor->second;
6494       break;
6495     }
6496     case TopAbs_VERTEX:
6497     {
6498       int nbEdges = 0;
6499       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6500       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6501       {
6502         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6503         for ( int di = -1; di <= 0; ++di )
6504         {
6505           size_t j = i + di;
6506           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6507           {
6508             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6509             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6510             if ( id2factor->second == 0 )
6511             {
6512               if ( j < i )
6513                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6514               else
6515                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6516             }
6517             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6518             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6519             gp_Vec du;
6520             curve->D1( u, p, du );
6521             double size2 = du.SquareMagnitude();
6522             if ( du.SquareMagnitude() > tol2 )
6523             {
6524               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6525               nbEdges++;
6526             }
6527             break;
6528           }
6529         }
6530       }
6531       if ( nbEdges > 0 )
6532         break;
6533     }
6534     // fall through
6535     default:
6536     {
6537       for ( int di = -1; di <= 1; di += 2 )
6538       {
6539         size_t j = i + di;
6540         if ( j < pathNodes.size() )
6541         {
6542           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6543           double size2 = dir.SquareMagnitude();
6544           if ( size2 > tol2 )
6545             tangent += dir.Divided( Sqrt( size2 )) * di;
6546         }
6547       }
6548     }
6549     } // switch ( shapeType )
6550
6551     if ( tangent.SquareMagnitude() < tol2 )
6552       return EXTR_CANT_GET_TANGENT;
6553
6554     point.myTgt = tangent;
6555
6556   } // loop on pathNodes
6557
6558
6559   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6560   TTElemOfElemListMap newElemsMap;
6561
6562   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6563
6564   return EXTR_OK;
6565 }
6566
6567 //=======================================================================
6568 //function : linearAngleVariation
6569 //purpose  : spread values over nbSteps
6570 //=======================================================================
6571
6572 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6573                                             list<double>& Angles)
6574 {
6575   int nbAngles = Angles.size();
6576   if( nbSteps > nbAngles && nbAngles > 0 )
6577   {
6578     vector<double> theAngles(nbAngles);
6579     theAngles.assign( Angles.begin(), Angles.end() );
6580
6581     list<double> res;
6582     double rAn2St = double( nbAngles ) / double( nbSteps );
6583     double angPrev = 0, angle;
6584     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6585     {
6586       double angCur = rAn2St * ( iSt+1 );
6587       double angCurFloor  = floor( angCur );
6588       double angPrevFloor = floor( angPrev );
6589       if ( angPrevFloor == angCurFloor )
6590         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6591       else {
6592         int iP = int( angPrevFloor );
6593         double angPrevCeil = ceil(angPrev);
6594         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6595
6596         int iC = int( angCurFloor );
6597         if ( iC < nbAngles )
6598           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6599
6600         iP = int( angPrevCeil );
6601         while ( iC-- > iP )
6602           angle += theAngles[ iC ];
6603       }
6604       res.push_back(angle);
6605       angPrev = angCur;
6606     }
6607     Angles.swap( res );
6608   }
6609 }
6610
6611 //=======================================================================
6612 //function : linearScaleVariation
6613 //purpose  : spread values over nbSteps 
6614 //=======================================================================
6615
6616 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6617                                             std::list<double>& theScales)
6618 {
6619   int nbScales = theScales.size();
6620   std::vector<double> myScales;
6621   myScales.reserve( theNbSteps );
6622   std::list<double>::const_iterator scale = theScales.begin();
6623   double prevScale = 1.0;
6624   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6625   {
6626     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6627     int    stDelta = Max( 1, iStep - myScales.size());
6628     double scDelta = ( *scale - prevScale ) / stDelta;
6629     for ( int iStep = 0; iStep < stDelta; ++iStep )
6630     {
6631       myScales.push_back( prevScale + scDelta );
6632       prevScale = myScales.back();
6633     }
6634     prevScale = *scale;
6635   }
6636   theScales.assign( myScales.begin(), myScales.end() );
6637 }
6638
6639 //================================================================================
6640 /*!
6641  * \brief Move or copy theElements applying theTrsf to their nodes
6642  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6643  *  \param theTrsf - transformation to apply
6644  *  \param theCopy - if true, create translated copies of theElems
6645  *  \param theMakeGroups - if true and theCopy, create translated groups
6646  *  \param theTargetMesh - mesh to copy translated elements into
6647  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6648  */
6649 //================================================================================
6650
6651 SMESH_MeshEditor::PGroupIDs
6652 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6653                              const gp_Trsf&     theTrsf,
6654                              const bool         theCopy,
6655                              const bool         theMakeGroups,
6656                              SMESH_Mesh*        theTargetMesh)
6657 {
6658   ClearLastCreated();
6659   myLastCreatedElems.reserve( theElems.size() );
6660
6661   bool needReverse = false;
6662   string groupPostfix;
6663   switch ( theTrsf.Form() ) {
6664   case gp_PntMirror:
6665     needReverse = true;
6666     groupPostfix = "mirrored";
6667     break;
6668   case gp_Ax1Mirror:
6669     groupPostfix = "mirrored";
6670     break;
6671   case gp_Ax2Mirror:
6672     needReverse = true;
6673     groupPostfix = "mirrored";
6674     break;
6675   case gp_Rotation:
6676     groupPostfix = "rotated";
6677     break;
6678   case gp_Translation:
6679     groupPostfix = "translated";
6680     break;
6681   case gp_Scale:
6682     groupPostfix = "scaled";
6683     break;
6684   case gp_CompoundTrsf: // different scale by axis
6685     groupPostfix = "scaled";
6686     break;
6687   default:
6688     needReverse = false;
6689     groupPostfix = "transformed";
6690   }
6691
6692   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6693   SMESHDS_Mesh* aMesh    = GetMeshDS();
6694
6695   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6696   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6697   SMESH_MeshEditor::ElemFeatures elemType;
6698
6699   // map old node to new one
6700   TNodeNodeMap nodeMap;
6701
6702   // elements sharing moved nodes; those of them which have all
6703   // nodes mirrored but are not in theElems are to be reversed
6704   TIDSortedElemSet inverseElemSet;
6705
6706   // source elements for each generated one
6707   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6708
6709   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6710   TIDSortedElemSet orphanNode;
6711
6712   if ( theElems.empty() ) // transform the whole mesh
6713   {
6714     // add all elements
6715     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6716     while ( eIt->more() ) theElems.insert( eIt->next() );
6717     // add orphan nodes
6718     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6719     while ( nIt->more() )
6720     {
6721       const SMDS_MeshNode* node = nIt->next();
6722       if ( node->NbInverseElements() == 0)
6723         orphanNode.insert( node );
6724     }
6725   }
6726
6727   // loop on elements to transform nodes : first orphan nodes then elems
6728   TIDSortedElemSet::iterator itElem;
6729   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6730   for (int i=0; i<2; i++)
6731     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6732     {
6733       const SMDS_MeshElement* elem = *itElem;
6734       if ( !elem )
6735         continue;
6736
6737       // loop on elem nodes
6738       double coord[3];
6739       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6740       while ( itN->more() )
6741       {
6742         const SMDS_MeshNode* node = cast2Node( itN->next() );
6743         // check if a node has been already transformed
6744         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6745           nodeMap.insert( make_pair ( node, node ));
6746         if ( !n2n_isnew.second )
6747           continue;
6748
6749         node->GetXYZ( coord );
6750         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6751         if ( theTargetMesh ) {
6752           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6753           n2n_isnew.first->second = newNode;
6754           myLastCreatedNodes.push_back(newNode);
6755           srcNodes.push_back( node );
6756         }
6757         else if ( theCopy ) {
6758           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6759           n2n_isnew.first->second = newNode;
6760           myLastCreatedNodes.push_back(newNode);
6761           srcNodes.push_back( node );
6762         }
6763         else {
6764           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6765           // node position on shape becomes invalid
6766           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6767             ( SMDS_SpacePosition::originSpacePosition() );
6768         }
6769
6770         // keep inverse elements
6771         if ( !theCopy && !theTargetMesh && needReverse ) {
6772           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6773           while ( invElemIt->more() ) {
6774             const SMDS_MeshElement* iel = invElemIt->next();
6775             inverseElemSet.insert( iel );
6776           }
6777         }
6778       }
6779     } // loop on elems in { &orphanNode, &theElems };
6780
6781   // either create new elements or reverse mirrored ones
6782   if ( !theCopy && !needReverse && !theTargetMesh )
6783     return PGroupIDs();
6784
6785   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6786
6787   // Replicate or reverse elements
6788
6789   std::vector<int> iForw;
6790   vector<const SMDS_MeshNode*> nodes;
6791   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6792   {
6793     const SMDS_MeshElement* elem = *itElem;
6794     if ( !elem ) continue;
6795
6796     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6797     size_t               nbNodes  = elem->NbNodes();
6798     if ( geomType == SMDSGeom_NONE ) continue; // node
6799
6800     nodes.resize( nbNodes );
6801
6802     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6803     {
6804       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6805       if ( !aPolyedre )
6806         continue;
6807       nodes.clear();
6808       bool allTransformed = true;
6809       int nbFaces = aPolyedre->NbFaces();
6810       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6811       {
6812         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6813         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6814         {
6815           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6816           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6817           if ( nodeMapIt == nodeMap.end() )
6818             allTransformed = false; // not all nodes transformed
6819           else
6820             nodes.push_back((*nodeMapIt).second);
6821         }
6822         if ( needReverse && allTransformed )
6823           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6824       }
6825       if ( !allTransformed )
6826         continue; // not all nodes transformed
6827     }
6828     else // ----------------------- the rest element types
6829     {
6830       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6831       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6832       const vector<int>&    i = needReverse ? iRev : iForw;
6833
6834       // find transformed nodes
6835       size_t iNode = 0;
6836       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6837       while ( itN->more() ) {
6838         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6839         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6840         if ( nodeMapIt == nodeMap.end() )
6841           break; // not all nodes transformed
6842         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6843       }
6844       if ( iNode != nbNodes )
6845         continue; // not all nodes transformed
6846     }
6847
6848     if ( editor ) {
6849       // copy in this or a new mesh
6850       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6851         srcElems.push_back( elem );
6852     }
6853     else {
6854       // reverse element as it was reversed by transformation
6855       if ( nbNodes > 2 )
6856         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6857     }
6858
6859   } // loop on elements
6860
6861   if ( editor && editor != this )
6862     myLastCreatedElems.swap( editor->myLastCreatedElems );
6863
6864   PGroupIDs newGroupIDs;
6865
6866   if ( ( theMakeGroups && theCopy ) ||
6867        ( theMakeGroups && theTargetMesh ) )
6868     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6869
6870   return newGroupIDs;
6871 }
6872
6873 //================================================================================
6874 /*!
6875  * \brief Make an offset mesh from a source 2D mesh
6876  *  \param [in] theElements - source faces
6877  *  \param [in] theValue - offset value
6878  *  \param [out] theTgtMesh - a mesh to add offset elements to
6879  *  \param [in] theMakeGroups - to generate groups
6880  *  \return PGroupIDs - IDs of created groups. NULL means failure
6881  */
6882 //================================================================================
6883
6884 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6885                                                       const double       theValue,
6886                                                       SMESH_Mesh*        theTgtMesh,
6887                                                       const bool         theMakeGroups,
6888                                                       const bool         theCopyElements,
6889                                                       const bool         theFixSelfIntersection)
6890 {
6891   SMESHDS_Mesh*    meshDS = GetMeshDS();
6892   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6893   SMESH_MeshEditor tgtEditor( theTgtMesh );
6894
6895   SMDS_ElemIteratorPtr eIt;
6896   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6897   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6898
6899   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6900   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6901   std::unique_ptr< SMDS_Mesh > offsetMesh
6902     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6903                                    theFixSelfIntersection,
6904                                    new2OldFaces, new2OldNodes ));
6905   if ( offsetMesh->NbElements() == 0 )
6906     return PGroupIDs(); // MakeOffset() failed
6907
6908
6909   if ( theTgtMesh == myMesh && !theCopyElements )
6910   {
6911     // clear the source elements
6912     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6913     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6914     while ( eIt->more() )
6915       meshDS->RemoveFreeElement( eIt->next(), 0 );
6916   }
6917
6918   // offsetMesh->Modified();
6919   // offsetMesh->CompactMesh(); // make IDs start from 1
6920
6921   // source elements for each generated one
6922   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6923   srcElems.reserve( new2OldFaces.size() );
6924   srcNodes.reserve( new2OldNodes.size() );
6925
6926   ClearLastCreated();
6927   myLastCreatedElems.reserve( new2OldFaces.size() );
6928   myLastCreatedNodes.reserve( new2OldNodes.size() );
6929
6930   // copy offsetMesh to theTgtMesh
6931
6932   smIdType idShift = meshDS->MaxNodeID();
6933   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6934     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6935     {
6936
6937       if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6938       {
6939         const SMDS_MeshNode* n2 =
6940           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6941         myLastCreatedNodes.push_back( n2 );
6942         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6943       }
6944     }
6945
6946   ElemFeatures elemType;
6947   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6948     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6949     {
6950       elemType.Init( f );
6951       elemType.myNodes.clear();
6952       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6953       {
6954         const SMDS_MeshNode* n2 = nIt->next();
6955         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6956       }
6957       tgtEditor.AddElement( elemType.myNodes, elemType );
6958       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6959     }
6960
6961   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6962
6963   PGroupIDs newGroupIDs;
6964   if ( theMakeGroups )
6965     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6966   else
6967     newGroupIDs.reset( new std::list< int > );
6968
6969   return newGroupIDs;
6970 }
6971
6972 //=======================================================================
6973 /*!
6974  * \brief Create groups of elements made during transformation
6975  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6976  *  \param elemGens - elements making corresponding myLastCreatedElems
6977  *  \param postfix - to push_back to names of new groups
6978  *  \param targetMesh - mesh to create groups in
6979  *  \param topPresent - is there are "top" elements that are created by sweeping
6980  */
6981 //=======================================================================
6982
6983 SMESH_MeshEditor::PGroupIDs
6984 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6985                                  const SMESH_SequenceOfElemPtr& elemGens,
6986                                  const std::string&             postfix,
6987                                  SMESH_Mesh*                    targetMesh,
6988                                  const bool                     topPresent)
6989 {
6990   PGroupIDs newGroupIDs( new list<int> );
6991   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6992
6993   // Sort existing groups by types and collect their names
6994
6995   // containers to store an old group and generated new ones;
6996   // 1st new group is for result elems of different type than a source one;
6997   // 2nd new group is for same type result elems ("top" group at extrusion)
6998   using boost::tuple;
6999   using boost::make_tuple;
7000   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7001   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7002   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7003   // group names
7004   set< string > groupNames;
7005
7006   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7007   if ( !groupIt->more() ) return newGroupIDs;
7008
7009   int newGroupID = mesh->GetGroupIds().back()+1;
7010   while ( groupIt->more() )
7011   {
7012     SMESH_Group * group = groupIt->next();
7013     if ( !group ) continue;
7014     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7015     if ( !groupDS || groupDS->IsEmpty() ) continue;
7016     groupNames.insert    ( group->GetName() );
7017     groupDS->SetStoreName( group->GetName() );
7018     const SMDSAbs_ElementType type = groupDS->GetType();
7019     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7020     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7021     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7022     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7023   }
7024
7025   // Loop on nodes and elements to add them in new groups
7026
7027   vector< const SMDS_MeshElement* > resultElems;
7028   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7029   {
7030     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7031     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7032     if ( gens.size() != elems.size() )
7033       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7034
7035     // loop on created elements
7036     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
7037     {
7038       const SMDS_MeshElement* sourceElem = gens[ iElem ];
7039       if ( !sourceElem ) {
7040         MESSAGE("generateGroups(): NULL source element");
7041         continue;
7042       }
7043       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7044       if ( groupsOldNew.empty() ) { // no groups of this type at all
7045         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7046           ++iElem; // skip all elements made by sourceElem
7047         continue;
7048       }
7049       // collect all elements made by the iElem-th sourceElem
7050       resultElems.clear();
7051       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
7052         if ( resElem != sourceElem )
7053           resultElems.push_back( resElem );
7054       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7055         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
7056           if ( resElem != sourceElem )
7057             resultElems.push_back( resElem );
7058
7059       const SMDS_MeshElement* topElem = 0;
7060       if ( isNodes ) // there must be a top element
7061       {
7062         topElem = resultElems.back();
7063         resultElems.pop_back();
7064       }
7065       else
7066       {
7067         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7068         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7069           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7070           {
7071             topElem = *resElemIt;
7072             *resElemIt = 0; // erase *resElemIt
7073             break;
7074           }
7075       }
7076       // add resultElems to groups originted from ones the sourceElem belongs to
7077       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7078       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7079       {
7080         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7081         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7082         {
7083           // fill in a new group
7084           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7085           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7086           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7087             if ( *resElemIt )
7088               newGroup.Add( *resElemIt );
7089
7090           // fill a "top" group
7091           if ( topElem )
7092           {
7093             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7094             newTopGroup.Add( topElem );
7095           }
7096         }
7097       }
7098     } // loop on created elements
7099   }// loop on nodes and elements
7100
7101   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7102
7103   list<int> topGrouIds;
7104   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7105   {
7106     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7107     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7108                                       orderedOldNewGroups[i]->get<2>() };
7109     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7110     {
7111       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7112       if ( newGroupDS->IsEmpty() )
7113       {
7114         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7115       }
7116       else
7117       {
7118         // set group type
7119         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7120
7121         // make a name
7122         const bool isTop = ( topPresent &&
7123                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7124                              is2nd );
7125
7126         string name = oldGroupDS->GetStoreName();
7127         { // remove trailing whitespaces (issue 22599)
7128           size_t size = name.size();
7129           while ( size > 1 && isspace( name[ size-1 ]))
7130             --size;
7131           if ( size != name.size() )
7132           {
7133             name.resize( size );
7134             oldGroupDS->SetStoreName( name.c_str() );
7135           }
7136         }
7137         if ( !targetMesh ) {
7138           string suffix = ( isTop ? "top": postfix.c_str() );
7139           name += "_";
7140           name += suffix;
7141           int nb = 1;
7142           while ( !groupNames.insert( name ).second ) // name exists
7143             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7144         }
7145         else if ( isTop ) {
7146           name += "_top";
7147         }
7148         newGroupDS->SetStoreName( name.c_str() );
7149
7150         // make a SMESH_Groups
7151         mesh->AddGroup( newGroupDS );
7152         if ( isTop )
7153           topGrouIds.push_back( newGroupDS->GetID() );
7154         else
7155           newGroupIDs->push_back( newGroupDS->GetID() );
7156       }
7157     }
7158   }
7159   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7160
7161   return newGroupIDs;
7162 }
7163
7164 //================================================================================
7165 /*!
7166  *  * \brief Return list of group of nodes close to each other within theTolerance
7167  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7168  *  *        an Octree algorithm
7169  *  \param [in,out] theNodes - the nodes to treat
7170  *  \param [in]     theTolerance - the tolerance
7171  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7172  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7173  *         corner and medium nodes in separate groups
7174  */
7175 //================================================================================
7176
7177 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7178                                             const double         theTolerance,
7179                                             TListOfListOfNodes & theGroupsOfNodes,
7180                                             bool                 theSeparateCornersAndMedium)
7181 {
7182   ClearLastCreated();
7183
7184   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7185        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7186        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7187     theSeparateCornersAndMedium = false;
7188
7189   TIDSortedNodeSet& corners = theNodes;
7190   TIDSortedNodeSet  medium;
7191
7192   if ( theNodes.empty() ) // get all nodes in the mesh
7193   {
7194     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7195     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7196     if ( theSeparateCornersAndMedium )
7197       while ( nIt->more() )
7198       {
7199         const SMDS_MeshNode* n = nIt->next();
7200         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7201         nodeSet->insert( nodeSet->end(), n );
7202       }
7203     else
7204       while ( nIt->more() )
7205         theNodes.insert( theNodes.end(), nIt->next() );
7206   }
7207   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7208   {
7209     TIDSortedNodeSet::iterator nIt = corners.begin();
7210     while ( nIt != corners.end() )
7211       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7212       {
7213         medium.insert( medium.end(), *nIt );
7214         corners.erase( nIt++ );
7215       }
7216       else
7217       {
7218         ++nIt;
7219       }
7220   }
7221
7222   if ( !corners.empty() )
7223     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7224   if ( !medium.empty() )
7225     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7226 }
7227
7228 //=======================================================================
7229 //function : SimplifyFace
7230 //purpose  : split a chain of nodes into several closed chains
7231 //=======================================================================
7232
7233 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7234                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7235                                     vector<int>&                         quantities) const
7236 {
7237   int nbNodes = faceNodes.size();
7238   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7239     --nbNodes;
7240   if ( nbNodes < 3 )
7241     return 0;
7242   size_t prevNbQuant = quantities.size();
7243
7244   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7245   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7246   map< const SMDS_MeshNode*, int >::iterator nInd;
7247
7248   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7249   simpleNodes.push_back( faceNodes[0] );
7250   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7251   {
7252     if ( faceNodes[ iCur ] != simpleNodes.back() )
7253     {
7254       int index = simpleNodes.size();
7255       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7256       int prevIndex = nInd->second;
7257       if ( prevIndex < index )
7258       {
7259         // a sub-loop found
7260         int loopLen = index - prevIndex;
7261         if ( loopLen > 2 )
7262         {
7263           // store the sub-loop
7264           quantities.push_back( loopLen );
7265           for ( int i = prevIndex; i < index; i++ )
7266             poly_nodes.push_back( simpleNodes[ i ]);
7267         }
7268         simpleNodes.resize( prevIndex+1 );
7269       }
7270       else
7271       {
7272         simpleNodes.push_back( faceNodes[ iCur ]);
7273       }
7274     }
7275   }
7276
7277   if ( simpleNodes.size() > 2 )
7278   {
7279     quantities.push_back( simpleNodes.size() );
7280     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7281   }
7282
7283   return quantities.size() - prevNbQuant;
7284 }
7285
7286 //=======================================================================
7287 //function : MergeNodes
7288 //purpose  : In each group, the cdr of nodes are substituted by the first one
7289 //           in all elements.
7290 //=======================================================================
7291
7292 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7293                                    const bool           theAvoidMakingHoles)
7294 {
7295   ClearLastCreated();
7296
7297   SMESHDS_Mesh* mesh = GetMeshDS();
7298
7299   TNodeNodeMap nodeNodeMap; // node to replace - new node
7300   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7301   list< smIdType > rmElemIds, rmNodeIds;
7302   vector< ElemFeatures > newElemDefs;
7303
7304   // Fill nodeNodeMap and elems
7305
7306   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7307   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7308   {
7309     list<const SMDS_MeshNode*>& nodes = *grIt;
7310     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7311     const SMDS_MeshNode* nToKeep = *nIt;
7312     for ( ++nIt; nIt != nodes.end(); nIt++ )
7313     {
7314       const SMDS_MeshNode* nToRemove = *nIt;
7315       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7316       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7317       while ( invElemIt->more() ) {
7318         const SMDS_MeshElement* elem = invElemIt->next();
7319         elems.insert(elem);
7320       }
7321     }
7322   }
7323
7324   // Apply recursive replacements (BUG 0020185)
7325   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7326   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7327   {
7328     const SMDS_MeshNode* nToKeep = nnIt->second;
7329     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7330     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7331     {
7332       nToKeep = nnIt_i->second;
7333       nnIt->second = nToKeep;
7334       nnIt_i = nodeNodeMap.find( nToKeep );
7335     }
7336   }
7337
7338   if ( theAvoidMakingHoles )
7339   {
7340     // find elements whose topology changes
7341
7342     vector<const SMDS_MeshElement*> pbElems;
7343     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7344     for ( ; eIt != elems.end(); ++eIt )
7345     {
7346       const SMDS_MeshElement* elem = *eIt;
7347       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7348       while ( itN->more() )
7349       {
7350         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7351         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7352         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7353         {
7354           // several nodes of elem stick
7355           pbElems.push_back( elem );
7356           break;
7357         }
7358       }
7359     }
7360     // exclude from merge nodes causing spoiling element
7361     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7362     {
7363       bool nodesExcluded = false;
7364       for ( size_t i = 0; i < pbElems.size(); ++i )
7365       {
7366         size_t prevNbMergeNodes = nodeNodeMap.size();
7367         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7368              prevNbMergeNodes < nodeNodeMap.size() )
7369           nodesExcluded = true;
7370       }
7371       if ( !nodesExcluded )
7372         break;
7373     }
7374   }
7375
7376   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7377   {
7378     const SMDS_MeshNode* nToRemove = nnIt->first;
7379     const SMDS_MeshNode* nToKeep   = nnIt->second;
7380     if ( nToRemove != nToKeep )
7381     {
7382       rmNodeIds.push_back( nToRemove->GetID() );
7383       AddToSameGroups( nToKeep, nToRemove, mesh );
7384       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7385       // w/o creating node in place of merged ones.
7386       SMDS_PositionPtr pos = nToRemove->GetPosition();
7387       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7388         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7389           sm->SetIsAlwaysComputed( true );
7390     }
7391   }
7392
7393   // Change element nodes or remove an element
7394
7395   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7396   for ( ; eIt != elems.end(); eIt++ )
7397   {
7398     const SMDS_MeshElement* elem = *eIt;
7399     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7400     bool                 marked = elem->isMarked();
7401
7402     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7403     if ( !keepElem )
7404       rmElemIds.push_back( elem->GetID() );
7405
7406     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7407     {
7408       bool elemChanged = false;
7409       if ( i == 0 )
7410       {
7411         if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7412           elemChanged = mesh->ChangePolyhedronNodes( elem,
7413                                                      newElemDefs[i].myNodes,
7414                                                      newElemDefs[i].myPolyhedQuantities );
7415         else
7416           elemChanged = mesh->ChangeElementNodes( elem,
7417                                                   & newElemDefs[i].myNodes[0],
7418                                                   newElemDefs[i].myNodes.size() );
7419       }
7420       if ( i > 0 || !elemChanged )
7421       {
7422         if ( i == 0 )
7423         {
7424           newElemDefs[i].SetID( elem->GetID() );
7425           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7426           if ( !keepElem ) rmElemIds.pop_back();
7427         }
7428         else
7429         {
7430           newElemDefs[i].SetID( -1 );
7431         }
7432         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7433         if ( sm && newElem )
7434           sm->AddElement( newElem );
7435         if ( elem != newElem )
7436           ReplaceElemInGroups( elem, newElem, mesh );
7437         if ( marked && newElem )
7438           newElem->setIsMarked( true );
7439       }
7440     }
7441   }
7442
7443   // Remove bad elements, then equal nodes (order important)
7444   Remove( rmElemIds, /*isNodes=*/false );
7445   Remove( rmNodeIds, /*isNodes=*/true );
7446
7447   return;
7448 }
7449
7450 //=======================================================================
7451 //function : applyMerge
7452 //purpose  : Compute new connectivity of an element after merging nodes
7453 //  \param [in] elems - the element
7454 //  \param [out] newElemDefs - definition(s) of result element(s)
7455 //  \param [inout] nodeNodeMap - nodes to merge
7456 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7457 //              after merging (but not degenerated), removes nodes causing
7458 //              the invalidity from \a nodeNodeMap.
7459 //  \return bool - true if the element should be removed
7460 //=======================================================================
7461
7462 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7463                                    vector< ElemFeatures >& newElemDefs,
7464                                    TNodeNodeMap&           nodeNodeMap,
7465                                    const bool              avoidMakingHoles )
7466 {
7467   bool toRemove = false; // to remove elem
7468   int nbResElems = 1;    // nb new elements
7469
7470   newElemDefs.resize(nbResElems);
7471   newElemDefs[0].Init( elem );
7472   newElemDefs[0].myNodes.clear();
7473
7474   set<const SMDS_MeshNode*> nodeSet;
7475   vector< const SMDS_MeshNode*>   curNodes;
7476   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7477   vector<int> iRepl;
7478
7479   const        int  nbNodes = elem->NbNodes();
7480   SMDSAbs_EntityType entity = elem->GetEntityType();
7481
7482   curNodes.resize( nbNodes );
7483   uniqueNodes.resize( nbNodes );
7484   iRepl.resize( nbNodes );
7485   int iUnique = 0, iCur = 0, nbRepl = 0;
7486
7487   // Get new seq of nodes
7488
7489   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7490   while ( itN->more() )
7491   {
7492     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7493
7494     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7495     if ( nnIt != nodeNodeMap.end() ) {
7496       n = (*nnIt).second;
7497     }
7498     curNodes[ iCur ] = n;
7499     bool isUnique = nodeSet.insert( n ).second;
7500     if ( isUnique )
7501       uniqueNodes[ iUnique++ ] = n;
7502     else
7503       iRepl[ nbRepl++ ] = iCur;
7504     iCur++;
7505   }
7506
7507   // Analyse element topology after replacement
7508
7509   int nbUniqueNodes = nodeSet.size();
7510   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7511   {
7512     toRemove = true;
7513     nbResElems = 0;
7514
7515     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7516     {
7517       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7518       int nbCorners = nbNodes / 2;
7519       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7520       {
7521         int iNext = ( iCur + 1 ) % nbCorners;
7522         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7523         {
7524           int iMedium = iCur + nbCorners;
7525           vector< const SMDS_MeshNode* >::iterator i =
7526             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7527                        uniqueNodes.end(),
7528                        curNodes[ iMedium ]);
7529           if ( i != uniqueNodes.end() )
7530           {
7531             --nbUniqueNodes;
7532             for ( ; i+1 != uniqueNodes.end(); ++i )
7533               *i = *(i+1);
7534           }
7535         }
7536       }
7537     }
7538
7539     switch ( entity )
7540     {
7541     case SMDSEntity_Polygon:
7542     case SMDSEntity_Quad_Polygon: // Polygon
7543     {
7544       ElemFeatures* elemType = & newElemDefs[0];
7545       const bool isQuad = elemType->myIsQuad;
7546       if ( isQuad )
7547         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7548           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7549
7550       // a polygon can divide into several elements
7551       vector<const SMDS_MeshNode *> polygons_nodes;
7552       vector<int> quantities;
7553       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7554       newElemDefs.resize( nbResElems );
7555       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7556       {
7557         ElemFeatures* elemType = & newElemDefs[iface];
7558         if ( iface ) elemType->Init( elem );
7559
7560         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7561         int nbNewNodes = quantities[iface];
7562         face_nodes.assign( polygons_nodes.begin() + inode,
7563                            polygons_nodes.begin() + inode + nbNewNodes );
7564         inode += nbNewNodes;
7565         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7566         {
7567           bool isValid = ( nbNewNodes % 2 == 0 );
7568           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7569             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7570           elemType->SetQuad( isValid );
7571           if ( isValid ) // put medium nodes after corners
7572             SMDS_MeshCell::applyInterlaceRev
7573               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7574                                                     nbNewNodes ), face_nodes );
7575         }
7576         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7577       }
7578       nbUniqueNodes = newElemDefs[0].myNodes.size();
7579       break;
7580     } // Polygon
7581
7582     case SMDSEntity_Polyhedra: // Polyhedral volume
7583     {
7584       if ( nbUniqueNodes >= 4 )
7585       {
7586         // each face has to be analyzed in order to check volume validity
7587         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7588         {
7589           toRemove = false;
7590           int nbFaces = aPolyedre->NbFaces();
7591
7592           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7593           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7594           vector<const SMDS_MeshNode *>  faceNodes;
7595           poly_nodes.clear();
7596           quantities.clear();
7597
7598           for (int iface = 1; iface <= nbFaces; iface++)
7599           {
7600             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7601             faceNodes.resize( nbFaceNodes );
7602             for (int inode = 1; inode <= nbFaceNodes; inode++)
7603             {
7604               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7605               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7606               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7607                 faceNode = (*nnIt).second;
7608               faceNodes[inode - 1] = faceNode;
7609             }
7610             SimplifyFace(faceNodes, poly_nodes, quantities);
7611           }
7612
7613           if ( quantities.size() > 3 )
7614           {
7615             // TODO: remove coincident faces
7616             nbResElems = 1;
7617             nbUniqueNodes = newElemDefs[0].myNodes.size();
7618           }
7619         }
7620       }
7621     }
7622     break;
7623
7624     // Regular elements
7625     // TODO not all the possible cases are solved. Find something more generic?
7626     case SMDSEntity_Edge: //////// EDGE
7627     case SMDSEntity_Triangle: //// TRIANGLE
7628     case SMDSEntity_Quad_Triangle:
7629     case SMDSEntity_Tetra:
7630     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7631     {
7632       break;
7633     }
7634     case SMDSEntity_Quad_Edge:
7635     {
7636       break;
7637     }
7638     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7639     {
7640       if ( nbUniqueNodes < 3 )
7641         toRemove = true;
7642       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7643         toRemove = true; // opposite nodes stick
7644       else
7645         toRemove = false;
7646       break;
7647     }
7648     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7649     {
7650       //   1    5    2
7651       //    +---+---+
7652       //    |       |
7653       //   4+       +6
7654       //    |       |
7655       //    +---+---+
7656       //   0    7    3
7657       if ( nbUniqueNodes == 6 &&
7658            iRepl[0] < 4       &&
7659            ( nbRepl == 1 || iRepl[1] >= 4 ))
7660       {
7661         toRemove = false;
7662       }
7663       break;
7664     }
7665     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7666     {
7667       //   1    5    2
7668       //    +---+---+
7669       //    |       |
7670       //   4+  8+   +6
7671       //    |       |
7672       //    +---+---+
7673       //   0    7    3
7674       if ( nbUniqueNodes == 7 &&
7675            iRepl[0] < 4       &&
7676            ( nbRepl == 1 || iRepl[1] != 8 ))
7677       {
7678         toRemove = false;
7679       }
7680       break;
7681     }
7682     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7683     {
7684       if ( nbUniqueNodes == 4 ) {
7685         // ---------------------------------> tetrahedron
7686         if ( curNodes[3] == curNodes[4] &&
7687              curNodes[3] == curNodes[5] ) {
7688           // top nodes stick
7689           toRemove = false;
7690         }
7691         else if ( curNodes[0] == curNodes[1] &&
7692                   curNodes[0] == curNodes[2] ) {
7693           // bottom nodes stick: set a top before
7694           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7695           uniqueNodes[ 0 ] = curNodes [ 5 ];
7696           uniqueNodes[ 1 ] = curNodes [ 4 ];
7697           uniqueNodes[ 2 ] = curNodes [ 3 ];
7698           toRemove = false;
7699         }
7700         else if (( curNodes[0] == curNodes[3] ) +
7701                  ( curNodes[1] == curNodes[4] ) +
7702                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7703           // a lateral face turns into a line
7704           toRemove = false;
7705         }
7706       }
7707       else if ( nbUniqueNodes == 5 ) {
7708         // PENTAHEDRON --------------------> pyramid
7709         if ( curNodes[0] == curNodes[3] )
7710         {
7711           uniqueNodes[ 0 ] = curNodes[ 1 ];
7712           uniqueNodes[ 1 ] = curNodes[ 4 ];
7713           uniqueNodes[ 2 ] = curNodes[ 5 ];
7714           uniqueNodes[ 3 ] = curNodes[ 2 ];
7715           uniqueNodes[ 4 ] = curNodes[ 0 ];
7716           toRemove = false;
7717         }
7718         if ( curNodes[1] == curNodes[4] )
7719         {
7720           uniqueNodes[ 0 ] = curNodes[ 0 ];
7721           uniqueNodes[ 1 ] = curNodes[ 2 ];
7722           uniqueNodes[ 2 ] = curNodes[ 5 ];
7723           uniqueNodes[ 3 ] = curNodes[ 3 ];
7724           uniqueNodes[ 4 ] = curNodes[ 1 ];
7725           toRemove = false;
7726         }
7727         if ( curNodes[2] == curNodes[5] )
7728         {
7729           uniqueNodes[ 0 ] = curNodes[ 0 ];
7730           uniqueNodes[ 1 ] = curNodes[ 3 ];
7731           uniqueNodes[ 2 ] = curNodes[ 4 ];
7732           uniqueNodes[ 3 ] = curNodes[ 1 ];
7733           uniqueNodes[ 4 ] = curNodes[ 2 ];
7734           toRemove = false;
7735         }
7736       }
7737       break;
7738     }
7739     case SMDSEntity_Hexa:
7740     {
7741       //////////////////////////////////// HEXAHEDRON
7742       SMDS_VolumeTool hexa (elem);
7743       hexa.SetExternalNormal();
7744       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7745         //////////////////////// HEX ---> tetrahedron
7746         for ( int iFace = 0; iFace < 6; iFace++ ) {
7747           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7748           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7749               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7750               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7751             // one face turns into a point ...
7752             int  pickInd = ind[ 0 ];
7753             int iOppFace = hexa.GetOppFaceIndex( iFace );
7754             ind = hexa.GetFaceNodesIndices( iOppFace );
7755             int nbStick = 0;
7756             uniqueNodes.clear();
7757             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7758               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7759                 nbStick++;
7760               else
7761                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7762             }
7763             if ( nbStick == 1 ) {
7764               // ... and the opposite one - into a triangle.
7765               // set a top node
7766               uniqueNodes.push_back( curNodes[ pickInd ]);
7767               toRemove = false;
7768             }
7769             break;
7770           }
7771         }
7772       }
7773       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7774         //////////////////////// HEX ---> prism
7775         int nbTria = 0, iTria[3];
7776         const int *ind; // indices of face nodes
7777         // look for triangular faces
7778         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7779           ind = hexa.GetFaceNodesIndices( iFace );
7780           TIDSortedNodeSet faceNodes;
7781           for ( iCur = 0; iCur < 4; iCur++ )
7782             faceNodes.insert( curNodes[ind[iCur]] );
7783           if ( faceNodes.size() == 3 )
7784             iTria[ nbTria++ ] = iFace;
7785         }
7786         // check if triangles are opposite
7787         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7788         {
7789           // set nodes of the bottom triangle
7790           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7791           vector<int> indB;
7792           for ( iCur = 0; iCur < 4; iCur++ )
7793             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7794               indB.push_back( ind[iCur] );
7795           if ( !hexa.IsForward() )
7796             std::swap( indB[0], indB[2] );
7797           for ( iCur = 0; iCur < 3; iCur++ )
7798             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7799           // set nodes of the top triangle
7800           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7801           for ( iCur = 0; iCur < 3; ++iCur )
7802             for ( int j = 0; j < 4; ++j )
7803               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7804               {
7805                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7806                 break;
7807               }
7808           toRemove = false;
7809           break;
7810         }
7811       }
7812       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7813         //////////////////// HEXAHEDRON ---> pyramid
7814         for ( int iFace = 0; iFace < 6; iFace++ ) {
7815           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7816           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7817               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7818               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7819             // one face turns into a point ...
7820             int iOppFace = hexa.GetOppFaceIndex( iFace );
7821             ind = hexa.GetFaceNodesIndices( iOppFace );
7822             uniqueNodes.clear();
7823             for ( iCur = 0; iCur < 4; iCur++ ) {
7824               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7825                 break;
7826               else
7827                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7828             }
7829             if ( uniqueNodes.size() == 4 ) {
7830               // ... and the opposite one is a quadrangle
7831               // set a top node
7832               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7833               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7834               toRemove = false;
7835             }
7836             break;
7837           }
7838         }
7839       }
7840
7841       if ( toRemove && nbUniqueNodes > 4 ) {
7842         ////////////////// HEXAHEDRON ---> polyhedron
7843         hexa.SetExternalNormal();
7844         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7845         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7846         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7847         quantities.reserve( 6 );     quantities.clear();
7848         for ( int iFace = 0; iFace < 6; iFace++ )
7849         {
7850           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7851           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7852                curNodes[ind[1]] == curNodes[ind[3]] )
7853           {
7854             quantities.clear();
7855             break; // opposite nodes stick
7856           }
7857           nodeSet.clear();
7858           for ( iCur = 0; iCur < 4; iCur++ )
7859           {
7860             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7861               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7862           }
7863           if ( nodeSet.size() < 3 )
7864             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7865           else
7866             quantities.push_back( nodeSet.size() );
7867         }
7868         if ( quantities.size() >= 4 )
7869         {
7870           nbResElems = 1;
7871           nbUniqueNodes = poly_nodes.size();
7872           newElemDefs[0].SetPoly(true);
7873         }
7874       }
7875       break;
7876     } // case HEXAHEDRON
7877
7878     default:
7879       toRemove = true;
7880
7881     } // switch ( entity )
7882
7883     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7884     {
7885       // erase from nodeNodeMap nodes whose merge spoils elem
7886       vector< const SMDS_MeshNode* > noMergeNodes;
7887       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7888       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7889         nodeNodeMap.erase( noMergeNodes[i] );
7890     }
7891     
7892   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7893
7894   uniqueNodes.resize( nbUniqueNodes );
7895
7896   if ( !toRemove && nbResElems == 0 )
7897     nbResElems = 1;
7898
7899   newElemDefs.resize( nbResElems );
7900
7901   return !toRemove;
7902 }
7903
7904
7905 // ========================================================
7906 // class   : ComparableElement
7907 // purpose : allow comparing elements basing on their nodes
7908 // ========================================================
7909
7910 struct ComparableElementHasher;
7911
7912 class ComparableElement : public boost::container::flat_set< smIdType >
7913 {
7914   typedef boost::container::flat_set< smIdType >  int_set;
7915
7916   const SMDS_MeshElement* myElem;
7917   smIdType                mySumID;
7918   mutable int             myGroupID;
7919
7920   friend ComparableElementHasher;
7921
7922 public:
7923
7924   ComparableElement( const SMDS_MeshElement* theElem ):
7925     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7926   {
7927     this->reserve( theElem->NbNodes() );
7928     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7929     {
7930       smIdType id = nodeIt->next()->GetID();
7931       mySumID += id;
7932       this->insert( id );
7933     }
7934   }
7935
7936   const SMDS_MeshElement* GetElem() const { return myElem; }
7937
7938   int& GroupID() const { return myGroupID; }
7939   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7940
7941   ComparableElement( const ComparableElement& theSource ) // move copy
7942     : int_set()
7943   {
7944     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7945     (int_set&) (*this ) = std::move( src );
7946     myElem    = src.myElem;
7947     mySumID   = src.mySumID;
7948     myGroupID = src.myGroupID;
7949   }
7950 };
7951
7952 struct ComparableElementHasher
7953 {
7954 #if OCC_VERSION_LARGE < 0x07080000
7955   static int HashCode(const ComparableElement& se, int limit )
7956   {
7957     return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7958   }
7959   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7960   {
7961     return ( se1 == se2 );
7962   }
7963 #else
7964   size_t operator()(const ComparableElement& se) const
7965   {
7966     return static_cast<size_t>(FromSmIdType<int>(se.mySumID));
7967   }
7968
7969   bool operator()(const ComparableElement& se1, const ComparableElement& se2) const
7970   {
7971     return ( se1 == se2 );
7972   }
7973 #endif
7974 };
7975
7976 //=======================================================================
7977 //function : FindEqualElements
7978 //purpose  : Return list of group of elements built on the same nodes.
7979 //           Search among theElements or in the whole mesh if theElements is empty
7980 //=======================================================================
7981
7982 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7983                                           TListOfListOfElementsID & theGroupsOfElementsID )
7984 {
7985   ClearLastCreated();
7986
7987   SMDS_ElemIteratorPtr elemIt;
7988   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7989   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7990
7991   typedef NCollection_Map< ComparableElement, ComparableElementHasher > TMapOfElements;
7992   typedef std::list<smIdType>                                           TGroupOfElems;
7993   TMapOfElements               mapOfElements;
7994   std::vector< TGroupOfElems > arrayOfGroups;
7995   TGroupOfElems                groupOfElems;
7996
7997   while ( elemIt->more() )
7998   {
7999     const SMDS_MeshElement* curElem = elemIt->next();
8000     if ( curElem->IsNull() )
8001       continue;
8002     ComparableElement      compElem = curElem;
8003     // check uniqueness
8004     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
8005     if ( elemInSet.GetElem() != curElem ) // coincident elem
8006     {
8007       int& iG = elemInSet.GroupID();
8008       if ( iG < 0 )
8009       {
8010         iG = arrayOfGroups.size();
8011         arrayOfGroups.push_back( groupOfElems );
8012         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
8013       }
8014       arrayOfGroups[ iG ].push_back( curElem->GetID() );
8015     }
8016   }
8017
8018   groupOfElems.clear();
8019   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8020   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8021   {
8022     if ( groupIt->size() > 1 ) {
8023       //groupOfElems.sort(); -- theElements are sorted already
8024       theGroupsOfElementsID.emplace_back( *groupIt );
8025     }
8026   }
8027 }
8028
8029 //=======================================================================
8030 //function : MergeElements
8031 //purpose  : In each given group, substitute all elements by the first one.
8032 //=======================================================================
8033
8034 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8035 {
8036   ClearLastCreated();
8037
8038   typedef list<smIdType> TListOfIDs;
8039   TListOfIDs rmElemIds; // IDs of elems to remove
8040
8041   SMESHDS_Mesh* aMesh = GetMeshDS();
8042
8043   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8044   while ( groupsIt != theGroupsOfElementsID.end() ) {
8045     TListOfIDs& aGroupOfElemID = *groupsIt;
8046     aGroupOfElemID.sort();
8047     int elemIDToKeep = aGroupOfElemID.front();
8048     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8049     aGroupOfElemID.pop_front();
8050     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8051     while ( idIt != aGroupOfElemID.end() ) {
8052       int elemIDToRemove = *idIt;
8053       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8054       // add the kept element in groups of removed one (PAL15188)
8055       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8056       rmElemIds.push_back( elemIDToRemove );
8057       ++idIt;
8058     }
8059     ++groupsIt;
8060   }
8061
8062   Remove( rmElemIds, false );
8063 }
8064
8065 //=======================================================================
8066 //function : MergeEqualElements
8067 //purpose  : Remove all but one of elements built on the same nodes.
8068 //=======================================================================
8069
8070 void SMESH_MeshEditor::MergeEqualElements()
8071 {
8072   TIDSortedElemSet aMeshElements; /* empty input ==
8073                                      to merge equal elements in the whole mesh */
8074   TListOfListOfElementsID aGroupsOfElementsID;
8075   FindEqualElements( aMeshElements, aGroupsOfElementsID );
8076   MergeElements( aGroupsOfElementsID );
8077 }
8078
8079 //=======================================================================
8080 //function : findAdjacentFace
8081 //purpose  :
8082 //=======================================================================
8083
8084 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8085                                                 const SMDS_MeshNode* n2,
8086                                                 const SMDS_MeshElement* elem)
8087 {
8088   TIDSortedElemSet elemSet, avoidSet;
8089   if ( elem )
8090     avoidSet.insert ( elem );
8091   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8092 }
8093
8094 //=======================================================================
8095 //function : findSegment
8096 //purpose  : Return a mesh segment by two nodes one of which can be medium
8097 //=======================================================================
8098
8099 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8100                                            const SMDS_MeshNode* n2)
8101 {
8102   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8103   while ( it->more() )
8104   {
8105     const SMDS_MeshElement* seg = it->next();
8106     if ( seg->GetNodeIndex( n2 ) >= 0 )
8107       return seg;
8108   }
8109   return 0;
8110 }
8111
8112 //=======================================================================
8113 //function : FindFreeBorder
8114 //purpose  :
8115 //=======================================================================
8116
8117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8118
8119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8120                                        const SMDS_MeshNode*             theSecondNode,
8121                                        const SMDS_MeshNode*             theLastNode,
8122                                        list< const SMDS_MeshNode* > &   theNodes,
8123                                        list< const SMDS_MeshElement* >& theFaces)
8124 {
8125   if ( !theFirstNode || !theSecondNode )
8126     return false;
8127   // find border face between theFirstNode and theSecondNode
8128   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8129   if ( !curElem )
8130     return false;
8131
8132   theFaces.push_back( curElem );
8133   theNodes.push_back( theFirstNode );
8134   theNodes.push_back( theSecondNode );
8135
8136   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8137   //TIDSortedElemSet foundElems;
8138   bool needTheLast = ( theLastNode != 0 );
8139
8140   vector<const SMDS_MeshNode*> nodes;
8141   
8142   while ( nStart != theLastNode ) {
8143     if ( nStart == theFirstNode )
8144       return !needTheLast;
8145
8146     // find all free border faces sharing nStart
8147
8148     list< const SMDS_MeshElement* > curElemList;
8149     list< const SMDS_MeshNode* >    nStartList;
8150     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8151     while ( invElemIt->more() ) {
8152       const SMDS_MeshElement* e = invElemIt->next();
8153       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8154       {
8155         // get nodes
8156         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8157                       SMDS_MeshElement::iterator() );
8158         nodes.push_back( nodes[ 0 ]);
8159
8160         // check 2 links
8161         int iNode = 0, nbNodes = nodes.size() - 1;
8162         for ( iNode = 0; iNode < nbNodes; iNode++ )
8163           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8164                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8165               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8166           {
8167             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8168             curElemList.push_back( e );
8169           }
8170       }
8171     }
8172     // analyse the found
8173
8174     int nbNewBorders = curElemList.size();
8175     if ( nbNewBorders == 0 ) {
8176       // no free border furthermore
8177       return !needTheLast;
8178     }
8179     else if ( nbNewBorders == 1 ) {
8180       // one more element found
8181       nIgnore = nStart;
8182       nStart = nStartList.front();
8183       curElem = curElemList.front();
8184       theFaces.push_back( curElem );
8185       theNodes.push_back( nStart );
8186     }
8187     else {
8188       // several continuations found
8189       list< const SMDS_MeshElement* >::iterator curElemIt;
8190       list< const SMDS_MeshNode* >::iterator nStartIt;
8191       // check if one of them reached the last node
8192       if ( needTheLast ) {
8193         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8194              curElemIt!= curElemList.end();
8195              curElemIt++, nStartIt++ )
8196           if ( *nStartIt == theLastNode ) {
8197             theFaces.push_back( *curElemIt );
8198             theNodes.push_back( *nStartIt );
8199             return true;
8200           }
8201       }
8202       // find the best free border by the continuations
8203       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8204       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8205       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8206            curElemIt!= curElemList.end();
8207            curElemIt++, nStartIt++ )
8208       {
8209         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8210         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8211         // find one more free border
8212         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8213           cNL->clear();
8214           cFL->clear();
8215         }
8216         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8217           // choice: clear a worse one
8218           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8219           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8220           contNodes[ iWorse ].clear();
8221           contFaces[ iWorse ].clear();
8222         }
8223       }
8224       if ( contNodes[0].empty() && contNodes[1].empty() )
8225         return false;
8226
8227       // push_back the best free border
8228       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8229       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8230       //theNodes.pop_back(); // remove nIgnore
8231       theNodes.pop_back(); // remove nStart
8232       //theFaces.pop_back(); // remove curElem
8233       theNodes.splice( theNodes.end(), *cNL );
8234       theFaces.splice( theFaces.end(), *cFL );
8235       return true;
8236
8237     } // several continuations found
8238   } // while ( nStart != theLastNode )
8239
8240   return true;
8241 }
8242
8243 //=======================================================================
8244 //function : CheckFreeBorderNodes
8245 //purpose  : Return true if the tree nodes are on a free border
8246 //=======================================================================
8247
8248 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8249                                             const SMDS_MeshNode* theNode2,
8250                                             const SMDS_MeshNode* theNode3)
8251 {
8252   list< const SMDS_MeshNode* > nodes;
8253   list< const SMDS_MeshElement* > faces;
8254   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8255 }
8256
8257 //=======================================================================
8258 //function : SewFreeBorder
8259 //purpose  :
8260 //warning  : for border-to-side sewing theSideSecondNode is considered as
8261 //           the last side node and theSideThirdNode is not used
8262 //=======================================================================
8263
8264 SMESH_MeshEditor::Sew_Error
8265 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8266                                  const SMDS_MeshNode* theBordSecondNode,
8267                                  const SMDS_MeshNode* theBordLastNode,
8268                                  const SMDS_MeshNode* theSideFirstNode,
8269                                  const SMDS_MeshNode* theSideSecondNode,
8270                                  const SMDS_MeshNode* theSideThirdNode,
8271                                  const bool           theSideIsFreeBorder,
8272                                  const bool           toCreatePolygons,
8273                                  const bool           toCreatePolyedrs)
8274 {
8275   ClearLastCreated();
8276
8277   Sew_Error aResult = SEW_OK;
8278
8279   // ====================================
8280   //    find side nodes and elements
8281   // ====================================
8282
8283   list< const SMDS_MeshNode* >    nSide[ 2 ];
8284   list< const SMDS_MeshElement* > eSide[ 2 ];
8285   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8286   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8287
8288   // Free border 1
8289   // --------------
8290   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8291                       nSide[0], eSide[0])) {
8292     MESSAGE(" Free Border 1 not found " );
8293     aResult = SEW_BORDER1_NOT_FOUND;
8294   }
8295   if (theSideIsFreeBorder) {
8296     // Free border 2
8297     // --------------
8298     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8299                         nSide[1], eSide[1])) {
8300       MESSAGE(" Free Border 2 not found " );
8301       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8302     }
8303   }
8304   if ( aResult != SEW_OK )
8305     return aResult;
8306
8307   if (!theSideIsFreeBorder) {
8308     // Side 2
8309     // --------------
8310
8311     // -------------------------------------------------------------------------
8312     // Algo:
8313     // 1. If nodes to merge are not coincident, move nodes of the free border
8314     //    from the coord sys defined by the direction from the first to last
8315     //    nodes of the border to the correspondent sys of the side 2
8316     // 2. On the side 2, find the links most co-directed with the correspondent
8317     //    links of the free border
8318     // -------------------------------------------------------------------------
8319
8320     // 1. Since sewing may break if there are volumes to split on the side 2,
8321     //    we won't move nodes but just compute new coordinates for them
8322     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8323     TNodeXYZMap nBordXYZ;
8324     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8325     list< const SMDS_MeshNode* >::iterator nBordIt;
8326
8327     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8328     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8329     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8330     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8331     double tol2 = 1.e-8;
8332     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8333     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8334       // Need node movement.
8335
8336       // find X and Z axes to create trsf
8337       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8338       gp_Vec X = Zs ^ Zb;
8339       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8340         // Zb || Zs
8341         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8342
8343       // coord systems
8344       gp_Ax3 toBordAx( Pb1, Zb, X );
8345       gp_Ax3 fromSideAx( Ps1, Zs, X );
8346       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8347       // set trsf
8348       gp_Trsf toBordSys, fromSide2Sys;
8349       toBordSys.SetTransformation( toBordAx );
8350       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8351       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8352
8353       // move
8354       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8355         const SMDS_MeshNode* n = *nBordIt;
8356         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8357         toBordSys.Transforms( xyz );
8358         fromSide2Sys.Transforms( xyz );
8359         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8360       }
8361     }
8362     else {
8363       // just insert nodes XYZ in the nBordXYZ map
8364       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365         const SMDS_MeshNode* n = *nBordIt;
8366         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8367       }
8368     }
8369
8370     // 2. On the side 2, find the links most co-directed with the correspondent
8371     //    links of the free border
8372
8373     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8374     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8375     sideNodes.push_back( theSideFirstNode );
8376
8377     bool hasVolumes = false;
8378     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8379     set<long> foundSideLinkIDs, checkedLinkIDs;
8380     SMDS_VolumeTool volume;
8381     //const SMDS_MeshNode* faceNodes[ 4 ];
8382
8383     const SMDS_MeshNode*    sideNode;
8384     const SMDS_MeshElement* sideElem  = 0;
8385     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8386     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8387     nBordIt = bordNodes.begin();
8388     nBordIt++;
8389     // border node position and border link direction to compare with
8390     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8391     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8392     // choose next side node by link direction or by closeness to
8393     // the current border node:
8394     bool searchByDir = ( *nBordIt != theBordLastNode );
8395     do {
8396       // find the next node on the Side 2
8397       sideNode = 0;
8398       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8399       long linkID;
8400       checkedLinkIDs.clear();
8401       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8402
8403       // loop on inverse elements of current node (prevSideNode) on the Side 2
8404       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8405       while ( invElemIt->more() )
8406       {
8407         const SMDS_MeshElement* elem = invElemIt->next();
8408         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8409         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8410         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8411         bool isVolume = volume.Set( elem );
8412         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8413         if ( isVolume ) // --volume
8414           hasVolumes = true;
8415         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8416           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8417           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8418           while ( nIt->more() ) {
8419             nodes[ iNode ] = cast2Node( nIt->next() );
8420             if ( nodes[ iNode++ ] == prevSideNode )
8421               iPrevNode = iNode - 1;
8422           }
8423           // there are 2 links to check
8424           nbNodes = 2;
8425         }
8426         else // --edge
8427           continue;
8428         // loop on links, to be precise, on the second node of links
8429         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8430           const SMDS_MeshNode* n = nodes[ iNode ];
8431           if ( isVolume ) {
8432             if ( !volume.IsLinked( n, prevSideNode ))
8433               continue;
8434           }
8435           else {
8436             if ( iNode ) // a node before prevSideNode
8437               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8438             else         // a node after prevSideNode
8439               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8440           }
8441           // check if this link was already used
8442           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8443           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8444           if (!isJustChecked &&
8445               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8446           {
8447             // test a link geometrically
8448             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8449             bool linkIsBetter = false;
8450             double dot = 0.0, dist = 0.0;
8451             if ( searchByDir ) { // choose most co-directed link
8452               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8453               linkIsBetter = ( dot > maxDot );
8454             }
8455             else { // choose link with the node closest to bordPos
8456               dist = ( nextXYZ - bordPos ).SquareModulus();
8457               linkIsBetter = ( dist < minDist );
8458             }
8459             if ( linkIsBetter ) {
8460               maxDot = dot;
8461               minDist = dist;
8462               linkID = iLink;
8463               sideNode = n;
8464               sideElem = elem;
8465             }
8466           }
8467         }
8468       } // loop on inverse elements of prevSideNode
8469
8470       if ( !sideNode ) {
8471         MESSAGE(" Can't find path by links of the Side 2 ");
8472         return SEW_BAD_SIDE_NODES;
8473       }
8474       sideNodes.push_back( sideNode );
8475       sideElems.push_back( sideElem );
8476       foundSideLinkIDs.insert ( linkID );
8477       prevSideNode = sideNode;
8478
8479       if ( *nBordIt == theBordLastNode )
8480         searchByDir = false;
8481       else {
8482         // find the next border link to compare with
8483         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8484         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8485         // move to next border node if sideNode is before forward border node (bordPos)
8486         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8487           prevBordNode = *nBordIt;
8488           nBordIt++;
8489           bordPos = nBordXYZ[ *nBordIt ];
8490           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8491           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8492         }
8493       }
8494     }
8495     while ( sideNode != theSideSecondNode );
8496
8497     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8498       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8499       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8500     }
8501   } // end nodes search on the side 2
8502
8503   // ============================
8504   // sew the border to the side 2
8505   // ============================
8506
8507   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8508   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8509
8510   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8511   if ( toMergeConformal && toCreatePolygons )
8512   {
8513     // do not merge quadrangles if polygons are OK (IPAL0052824)
8514     eIt[0] = eSide[0].begin();
8515     eIt[1] = eSide[1].begin();
8516     bool allQuads[2] = { true, true };
8517     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8518       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8519         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8520     }
8521     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8522   }
8523
8524   TListOfListOfNodes nodeGroupsToMerge;
8525   if (( toMergeConformal ) ||
8526       ( theSideIsFreeBorder && !theSideThirdNode )) {
8527
8528     // all nodes are to be merged
8529
8530     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8531          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8532          nIt[0]++, nIt[1]++ )
8533     {
8534       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8535       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8536       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8537     }
8538   }
8539   else {
8540
8541     // insert new nodes into the border and the side to get equal nb of segments
8542
8543     // get normalized parameters of nodes on the borders
8544     vector< double > param[ 2 ];
8545     param[0].resize( maxNbNodes );
8546     param[1].resize( maxNbNodes );
8547     int iNode, iBord;
8548     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8549       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8550       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8551       const SMDS_MeshNode* nPrev = *nIt;
8552       double bordLength = 0;
8553       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8554         const SMDS_MeshNode* nCur = *nIt;
8555         gp_XYZ segment (nCur->X() - nPrev->X(),
8556                         nCur->Y() - nPrev->Y(),
8557                         nCur->Z() - nPrev->Z());
8558         double segmentLen = segment.Modulus();
8559         bordLength += segmentLen;
8560         param[ iBord ][ iNode ] = bordLength;
8561         nPrev = nCur;
8562       }
8563       // normalize within [0,1]
8564       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8565         param[ iBord ][ iNode ] /= bordLength;
8566       }
8567     }
8568
8569     // loop on border segments
8570     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8571     int i[ 2 ] = { 0, 0 };
8572     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8573     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8574
8575     // element can be split while iterating on border if it has two edges in the border
8576     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8577     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8578
8579     TElemOfNodeListMap insertMap;
8580     TElemOfNodeListMap::iterator insertMapIt;
8581     // insertMap is
8582     // key:   elem to insert nodes into
8583     // value: 2 nodes to insert between + nodes to be inserted
8584     do {
8585       bool next[ 2 ] = { false, false };
8586
8587       // find min adjacent segment length after sewing
8588       double nextParam = 10., prevParam = 0;
8589       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8590         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8591           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8592         if ( i[ iBord ] > 0 )
8593           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8594       }
8595       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8596       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8597       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8598
8599       // choose to insert or to merge nodes
8600       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8601       if ( Abs( du ) <= minSegLen * 0.2 ) {
8602         // merge
8603         // ------
8604         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8605         const SMDS_MeshNode* n0 = *nIt[0];
8606         const SMDS_MeshNode* n1 = *nIt[1];
8607         nodeGroupsToMerge.back().push_back( n1 );
8608         nodeGroupsToMerge.back().push_back( n0 );
8609         // position of node of the border changes due to merge
8610         param[ 0 ][ i[0] ] += du;
8611         // move n1 for the sake of elem shape evaluation during insertion.
8612         // n1 will be removed by MergeNodes() anyway
8613         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8614         next[0] = next[1] = true;
8615       }
8616       else {
8617         // insert
8618         // ------
8619         int intoBord = ( du < 0 ) ? 0 : 1;
8620         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8621         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8622         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8623         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8624         if ( intoBord == 1 ) {
8625           // move node of the border to be on a link of elem of the side
8626           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8627           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8628           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8629           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8630         }
8631         elemReplaceMapIt = elemReplaceMap.find( elem );
8632         if ( elemReplaceMapIt != elemReplaceMap.end() )
8633           elem = elemReplaceMapIt->second;
8634
8635         insertMapIt = insertMap.find( elem );
8636         bool  notFound = ( insertMapIt == insertMap.end() );
8637         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8638         if ( otherLink ) {
8639           // insert into another link of the same element:
8640           // 1. perform insertion into the other link of the elem
8641           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8642           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8643           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8644           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8645           // 2. perform insertion into the link of adjacent faces
8646           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8647             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8648           }
8649           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8650             InsertNodesIntoLink( seg, n12, n22, nodeList );
8651           }
8652           if (toCreatePolyedrs) {
8653             // perform insertion into the links of adjacent volumes
8654             UpdateVolumes(n12, n22, nodeList);
8655           }
8656           // 3. find an element appeared on n1 and n2 after the insertion
8657           insertMap.erase( insertMapIt );
8658           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8659           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8660           elem = elem2;
8661         }
8662         if ( notFound || otherLink ) {
8663           // add element and nodes of the side into the insertMap
8664           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8665           (*insertMapIt).second.push_back( n1 );
8666           (*insertMapIt).second.push_back( n2 );
8667         }
8668         // add node to be inserted into elem
8669         (*insertMapIt).second.push_back( nIns );
8670         next[ 1 - intoBord ] = true;
8671       }
8672
8673       // go to the next segment
8674       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8675         if ( next[ iBord ] ) {
8676           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8677             eIt[ iBord ]++;
8678           nPrev[ iBord ] = *nIt[ iBord ];
8679           nIt[ iBord ]++; i[ iBord ]++;
8680         }
8681       }
8682     }
8683     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8684
8685     // perform insertion of nodes into elements
8686
8687     for (insertMapIt = insertMap.begin();
8688          insertMapIt != insertMap.end();
8689          insertMapIt++ )
8690     {
8691       const SMDS_MeshElement* elem = (*insertMapIt).first;
8692       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8693       if ( nodeList.size() < 3 ) continue;
8694       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8695       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8696
8697       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8698
8699       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8700         InsertNodesIntoLink( seg, n1, n2, nodeList );
8701       }
8702
8703       if ( !theSideIsFreeBorder ) {
8704         // look for and insert nodes into the faces adjacent to elem
8705         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8706           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8707         }
8708       }
8709       if (toCreatePolyedrs) {
8710         // perform insertion into the links of adjacent volumes
8711         UpdateVolumes(n1, n2, nodeList);
8712       }
8713     }
8714   } // end: insert new nodes
8715
8716   MergeNodes ( nodeGroupsToMerge );
8717
8718
8719   // Remove coincident segments
8720
8721   // get new segments
8722   TIDSortedElemSet segments;
8723   SMESH_SequenceOfElemPtr newFaces;
8724   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8725   {
8726     if ( !myLastCreatedElems[i] ) continue;
8727     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8728       segments.insert( segments.end(), myLastCreatedElems[i] );
8729     else
8730       newFaces.push_back( myLastCreatedElems[i] );
8731   }
8732   // get segments adjacent to merged nodes
8733   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8734   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8735   {
8736     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8737     if ( nodes.front()->IsNull() ) continue;
8738     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8739     while ( segIt->more() )
8740       segments.insert( segIt->next() );
8741   }
8742
8743   // find coincident
8744   TListOfListOfElementsID equalGroups;
8745   if ( !segments.empty() )
8746     FindEqualElements( segments, equalGroups );
8747   if ( !equalGroups.empty() )
8748   {
8749     // remove from segments those that will be removed
8750     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8751     for ( ; itGroups != equalGroups.end(); ++itGroups )
8752     {
8753       list< smIdType >& group = *itGroups;
8754       list< smIdType >::iterator id = group.begin();
8755       for ( ++id; id != group.end(); ++id )
8756         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8757           segments.erase( seg );
8758     }
8759     // remove equal segments
8760     MergeElements( equalGroups );
8761
8762     // restore myLastCreatedElems
8763     myLastCreatedElems = newFaces;
8764     TIDSortedElemSet::iterator seg = segments.begin();
8765     for ( ; seg != segments.end(); ++seg )
8766       myLastCreatedElems.push_back( *seg );
8767   }
8768
8769   return aResult;
8770 }
8771
8772 //=======================================================================
8773 //function : InsertNodesIntoLink
8774 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8775 //           and theBetweenNode2 and split theElement
8776 //=======================================================================
8777
8778 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8779                                            const SMDS_MeshNode*        theBetweenNode1,
8780                                            const SMDS_MeshNode*        theBetweenNode2,
8781                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8782                                            const bool                  toCreatePoly)
8783 {
8784   if ( !theElement ) return;
8785
8786   SMESHDS_Mesh *aMesh = GetMeshDS();
8787   vector<const SMDS_MeshElement*> newElems;
8788
8789   if ( theElement->GetType() == SMDSAbs_Edge )
8790   {
8791     theNodesToInsert.push_front( theBetweenNode1 );
8792     theNodesToInsert.push_back ( theBetweenNode2 );
8793     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8794     const SMDS_MeshNode* n1 = *n;
8795     for ( ++n; n != theNodesToInsert.end(); ++n )
8796     {
8797       const SMDS_MeshNode* n2 = *n;
8798       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8799         AddToSameGroups( seg, theElement, aMesh );
8800       else
8801         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8802       n1 = n2;
8803     }
8804     theNodesToInsert.pop_front();
8805     theNodesToInsert.pop_back();
8806
8807     if ( theElement->IsQuadratic() ) // add a not split part
8808     {
8809       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8810                                           theElement->end_nodes() );
8811       int iOther = 0, nbN = nodes.size();
8812       for ( ; iOther < nbN; ++iOther )
8813         if ( nodes[iOther] != theBetweenNode1 &&
8814              nodes[iOther] != theBetweenNode2 )
8815           break;
8816       if      ( iOther == 0 )
8817       {
8818         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8819           AddToSameGroups( seg, theElement, aMesh );
8820         else
8821           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8822       }
8823       else if ( iOther == 2 )
8824       {
8825         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8826           AddToSameGroups( seg, theElement, aMesh );
8827         else
8828           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8829       }
8830     }
8831     // treat new elements
8832     for ( size_t i = 0; i < newElems.size(); ++i )
8833       if ( newElems[i] )
8834       {
8835         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8836         myLastCreatedElems.push_back( newElems[i] );
8837       }
8838     ReplaceElemInGroups( theElement, newElems, aMesh );
8839     aMesh->RemoveElement( theElement );
8840     return;
8841
8842   } // if ( theElement->GetType() == SMDSAbs_Edge )
8843
8844   const SMDS_MeshElement* theFace = theElement;
8845   if ( theFace->GetType() != SMDSAbs_Face ) return;
8846
8847   // find indices of 2 link nodes and of the rest nodes
8848   int iNode = 0, il1, il2, i3, i4;
8849   il1 = il2 = i3 = i4 = -1;
8850   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8851
8852   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8853   while ( nodeIt->more() ) {
8854     const SMDS_MeshNode* n = nodeIt->next();
8855     if ( n == theBetweenNode1 )
8856       il1 = iNode;
8857     else if ( n == theBetweenNode2 )
8858       il2 = iNode;
8859     else if ( i3 < 0 )
8860       i3 = iNode;
8861     else
8862       i4 = iNode;
8863     nodes[ iNode++ ] = n;
8864   }
8865   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8866     return ;
8867
8868   // arrange link nodes to go one after another regarding the face orientation
8869   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8870   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8871   if ( reverse ) {
8872     iNode = il1;
8873     il1 = il2;
8874     il2 = iNode;
8875     aNodesToInsert.reverse();
8876   }
8877   // check that not link nodes of a quadrangles are in good order
8878   int nbFaceNodes = theFace->NbNodes();
8879   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8880     iNode = i3;
8881     i3 = i4;
8882     i4 = iNode;
8883   }
8884
8885   if (toCreatePoly || theFace->IsPoly()) {
8886
8887     iNode = 0;
8888     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8889
8890     // add nodes of face up to first node of link
8891     bool isFLN = false;
8892     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8893     while ( nodeIt->more() && !isFLN ) {
8894       const SMDS_MeshNode* n = nodeIt->next();
8895       poly_nodes[iNode++] = n;
8896       isFLN = ( n == nodes[il1] );
8897     }
8898     // add nodes to insert
8899     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8900     for (; nIt != aNodesToInsert.end(); nIt++) {
8901       poly_nodes[iNode++] = *nIt;
8902     }
8903     // add nodes of face starting from last node of link
8904     while ( nodeIt->more() ) {
8905       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8906       poly_nodes[iNode++] = n;
8907     }
8908
8909     // make a new face
8910     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8911   }
8912
8913   else if ( !theFace->IsQuadratic() )
8914   {
8915     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8916     int nbLinkNodes = 2 + aNodesToInsert.size();
8917     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8918     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8919     linkNodes[ 0 ] = nodes[ il1 ];
8920     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8921     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923       linkNodes[ iNode++ ] = *nIt;
8924     }
8925     // decide how to split a quadrangle: compare possible variants
8926     // and choose which of splits to be a quadrangle
8927     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8928     if ( nbFaceNodes == 3 ) {
8929       iBestQuad = nbSplits;
8930       i4 = i3;
8931     }
8932     else if ( nbFaceNodes == 4 ) {
8933       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8934       double aBestRate = DBL_MAX;
8935       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8936         i1 = 0; i2 = 1;
8937         double aBadRate = 0;
8938         // evaluate elements quality
8939         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8940           if ( iSplit == iQuad ) {
8941             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8942                                    linkNodes[ i2++ ],
8943                                    nodes[ i3 ],
8944                                    nodes[ i4 ]);
8945             aBadRate += getBadRate( &quad, aCrit );
8946           }
8947           else {
8948             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8949                                    linkNodes[ i2++ ],
8950                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8951             aBadRate += getBadRate( &tria, aCrit );
8952           }
8953         }
8954         // choice
8955         if ( aBadRate < aBestRate ) {
8956           iBestQuad = iQuad;
8957           aBestRate = aBadRate;
8958         }
8959       }
8960     }
8961
8962     // create new elements
8963     i1 = 0; i2 = 1;
8964     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8965     {
8966       if ( iSplit == iBestQuad )
8967         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8968                                             linkNodes[ i2++ ],
8969                                             nodes[ i3 ],
8970                                             nodes[ i4 ]));
8971       else
8972         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8973                                             linkNodes[ i2++ ],
8974                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8975     }
8976
8977     const SMDS_MeshNode* newNodes[ 4 ];
8978     newNodes[ 0 ] = linkNodes[ i1 ];
8979     newNodes[ 1 ] = linkNodes[ i2 ];
8980     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8981     newNodes[ 3 ] = nodes[ i4 ];
8982     if (iSplit == iBestQuad)
8983       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8984     else
8985       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8986
8987   } // end if(!theFace->IsQuadratic())
8988
8989   else { // theFace is quadratic
8990     // we have to split theFace on simple triangles and one simple quadrangle
8991     int tmp = il1/2;
8992     int nbshift = tmp*2;
8993     // shift nodes in nodes[] by nbshift
8994     int i,j;
8995     for(i=0; i<nbshift; i++) {
8996       const SMDS_MeshNode* n = nodes[0];
8997       for(j=0; j<nbFaceNodes-1; j++) {
8998         nodes[j] = nodes[j+1];
8999       }
9000       nodes[nbFaceNodes-1] = n;
9001     }
9002     il1 = il1 - nbshift;
9003     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9004     //   n0      n1     n2    n0      n1     n2
9005     //     +-----+-----+        +-----+-----+
9006     //      \         /         |           |
9007     //       \       /          |           |
9008     //      n5+     +n3       n7+           +n3
9009     //         \   /            |           |
9010     //          \ /             |           |
9011     //           +              +-----+-----+
9012     //           n4           n6      n5     n4
9013
9014     // create new elements
9015     int n1,n2,n3;
9016     if ( nbFaceNodes == 6 ) { // quadratic triangle
9017       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9018       if ( theFace->IsMediumNode(nodes[il1]) ) {
9019         // create quadrangle
9020         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
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[5] ));
9028         n1 = 0;
9029         n2 = 1;
9030         n3 = 5;
9031       }
9032     }
9033     else { // nbFaceNodes==8 - quadratic quadrangle
9034       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9035       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9036       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9037       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9038         // create quadrangle
9039         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9040         n1 = 1;
9041         n2 = 2;
9042         n3 = 3;
9043       }
9044       else {
9045         // create quadrangle
9046         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9047         n1 = 0;
9048         n2 = 1;
9049         n3 = 7;
9050       }
9051     }
9052     // create needed triangles using n1,n2,n3 and inserted nodes
9053     int nbn = 2 + aNodesToInsert.size();
9054     vector<const SMDS_MeshNode*> aNodes(nbn);
9055     aNodes[0    ] = nodes[n1];
9056     aNodes[nbn-1] = nodes[n2];
9057     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9058     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9059       aNodes[iNode++] = *nIt;
9060     }
9061     for ( i = 1; i < nbn; i++ )
9062       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9063   }
9064
9065   // remove the old face
9066   for ( size_t i = 0; i < newElems.size(); ++i )
9067     if ( newElems[i] )
9068     {
9069       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9070       myLastCreatedElems.push_back( newElems[i] );
9071     }
9072   ReplaceElemInGroups( theFace, newElems, aMesh );
9073   aMesh->RemoveElement(theFace);
9074
9075 } // InsertNodesIntoLink()
9076
9077 //=======================================================================
9078 //function : UpdateVolumes
9079 //purpose  :
9080 //=======================================================================
9081
9082 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9083                                       const SMDS_MeshNode*        theBetweenNode2,
9084                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9085 {
9086   ClearLastCreated();
9087
9088   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9089   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9090     const SMDS_MeshElement* elem = invElemIt->next();
9091
9092     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9093     SMDS_VolumeTool aVolume (elem);
9094     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9095       continue;
9096
9097     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9098     int iface, nbFaces = aVolume.NbFaces();
9099     vector<const SMDS_MeshNode *> poly_nodes;
9100     vector<int> quantities (nbFaces);
9101
9102     for (iface = 0; iface < nbFaces; iface++) {
9103       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9104       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9105       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9106
9107       for (int inode = 0; inode < nbFaceNodes; inode++) {
9108         poly_nodes.push_back(faceNodes[inode]);
9109
9110         if (nbInserted == 0) {
9111           if (faceNodes[inode] == theBetweenNode1) {
9112             if (faceNodes[inode + 1] == theBetweenNode2) {
9113               nbInserted = theNodesToInsert.size();
9114
9115               // add nodes to insert
9116               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9117               for (; nIt != theNodesToInsert.end(); nIt++) {
9118                 poly_nodes.push_back(*nIt);
9119               }
9120             }
9121           }
9122           else if (faceNodes[inode] == theBetweenNode2) {
9123             if (faceNodes[inode + 1] == theBetweenNode1) {
9124               nbInserted = theNodesToInsert.size();
9125
9126               // add nodes to insert in reversed order
9127               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9128               nIt--;
9129               for (; nIt != theNodesToInsert.begin(); nIt--) {
9130                 poly_nodes.push_back(*nIt);
9131               }
9132               poly_nodes.push_back(*nIt);
9133             }
9134           }
9135           else {
9136           }
9137         }
9138       }
9139       quantities[iface] = nbFaceNodes + nbInserted;
9140     }
9141
9142     // Replace the volume
9143     SMESHDS_Mesh *aMesh = GetMeshDS();
9144
9145     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9146     {
9147       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9148       myLastCreatedElems.push_back( newElem );
9149       ReplaceElemInGroups( elem, newElem, aMesh );
9150     }
9151     aMesh->RemoveElement( elem );
9152   }
9153 }
9154
9155 namespace
9156 {
9157   //================================================================================
9158   /*!
9159    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9160    */
9161   //================================================================================
9162
9163   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9164                            vector<const SMDS_MeshNode *> & nodes,
9165                            vector<int> &                   nbNodeInFaces )
9166   {
9167     nodes.clear();
9168     nbNodeInFaces.clear();
9169     SMDS_VolumeTool vTool ( elem );
9170     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9171     {
9172       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9173       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9174       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9175     }
9176   }
9177 }
9178
9179 //=======================================================================
9180 /*!
9181  * \brief Convert elements contained in a sub-mesh to quadratic
9182  * \return int - nb of checked elements
9183  */
9184 //=======================================================================
9185
9186 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9187                                                   SMESH_MesherHelper& theHelper,
9188                                                   const bool          theForce3d)
9189 {
9190   //MESSAGE("convertElemToQuadratic");
9191   smIdType nbElem = 0;
9192   if( !theSm ) return nbElem;
9193
9194   vector<int> nbNodeInFaces;
9195   vector<const SMDS_MeshNode *> nodes;
9196   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9197   while(ElemItr->more())
9198   {
9199     nbElem++;
9200     const SMDS_MeshElement* elem = ElemItr->next();
9201     if( !elem ) continue;
9202
9203     // analyse a necessity of conversion
9204     const SMDSAbs_ElementType aType = elem->GetType();
9205     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9206       continue;
9207     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9208     bool hasCentralNodes = false;
9209     if ( elem->IsQuadratic() )
9210     {
9211       bool alreadyOK;
9212       switch ( aGeomType ) {
9213       case SMDSEntity_Quad_Triangle:
9214       case SMDSEntity_Quad_Quadrangle:
9215       case SMDSEntity_Quad_Hexa:
9216       case SMDSEntity_Quad_Penta:
9217         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9218
9219       case SMDSEntity_BiQuad_Triangle:
9220       case SMDSEntity_BiQuad_Quadrangle:
9221       case SMDSEntity_TriQuad_Hexa:
9222       case SMDSEntity_BiQuad_Penta:
9223         alreadyOK = theHelper.GetIsBiQuadratic();
9224         hasCentralNodes = true;
9225         break;
9226       default:
9227         alreadyOK = true;
9228       }
9229       // take into account already present medium nodes
9230       switch ( aType ) {
9231       case SMDSAbs_Volume:
9232         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9233       case SMDSAbs_Face:
9234         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9235       case SMDSAbs_Edge:
9236         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9237       default:;
9238       }
9239       if ( alreadyOK )
9240         continue;
9241     }
9242     // get elem data needed to re-create it
9243     //
9244     const smIdType id = elem->GetID();
9245     const int nbNodes = elem->NbCornerNodes();
9246     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9247     if ( aGeomType == SMDSEntity_Polyhedra )
9248       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9249     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9250       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9251
9252     // remove a linear element
9253     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9254
9255     // remove central nodes of biquadratic elements (biquad->quad conversion)
9256     if ( hasCentralNodes )
9257       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9258         if ( nodes[i]->NbInverseElements() == 0 )
9259           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9260
9261     const SMDS_MeshElement* NewElem = 0;
9262
9263     switch( aType )
9264     {
9265     case SMDSAbs_Edge :
9266     {
9267       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9268       break;
9269     }
9270     case SMDSAbs_Face :
9271     {
9272       switch(nbNodes)
9273       {
9274       case 3:
9275         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9276         break;
9277       case 4:
9278         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9279         break;
9280       default:
9281         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9282       }
9283       break;
9284     }
9285     case SMDSAbs_Volume :
9286     {
9287       switch( aGeomType )
9288       {
9289       case SMDSEntity_Tetra:
9290         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9291         break;
9292       case SMDSEntity_Pyramid:
9293         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9294         break;
9295       case SMDSEntity_Penta:
9296       case SMDSEntity_Quad_Penta:
9297       case SMDSEntity_BiQuad_Penta:
9298         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9299         break;
9300       case SMDSEntity_Hexa:
9301       case SMDSEntity_Quad_Hexa:
9302       case SMDSEntity_TriQuad_Hexa:
9303         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9304                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9305         break;
9306       case SMDSEntity_Hexagonal_Prism:
9307       default:
9308         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9309       }
9310       break;
9311     }
9312     default :
9313       continue;
9314     }
9315     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9316     if( NewElem && NewElem->getshapeId() < 1 )
9317       theSm->AddElement( NewElem );
9318   }
9319   return nbElem;
9320 }
9321 //=======================================================================
9322 //function : ConvertToQuadratic
9323 //purpose  :
9324 //=======================================================================
9325
9326 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9327 {
9328   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9329   SMESHDS_Mesh* meshDS = GetMeshDS();
9330
9331   SMESH_MesherHelper aHelper(*myMesh);
9332
9333   aHelper.SetIsQuadratic( true );
9334   aHelper.SetIsBiQuadratic( theToBiQuad );
9335   aHelper.SetElementsOnShape(true);
9336   aHelper.ToFixNodeParameters( true );
9337
9338   // convert elements assigned to sub-meshes
9339   smIdType nbCheckedElems = 0;
9340   if ( myMesh->HasShapeToMesh() )
9341   {
9342     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9343     {
9344       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9345       while ( smIt->more() ) {
9346         SMESH_subMesh* sm = smIt->next();
9347         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9348           aHelper.SetSubShape( sm->GetSubShape() );
9349           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9350         }
9351       }
9352     }
9353   }
9354
9355   // convert elements NOT assigned to sub-meshes
9356   smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9357   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9358   {
9359     aHelper.SetElementsOnShape(false);
9360     SMESHDS_SubMesh *smDS = 0;
9361
9362     // convert edges
9363     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9364     while( aEdgeItr->more() )
9365     {
9366       const SMDS_MeshEdge* edge = aEdgeItr->next();
9367       if ( !edge->IsQuadratic() )
9368       {
9369         smIdType                  id = edge->GetID();
9370         const SMDS_MeshNode* n1 = edge->GetNode(0);
9371         const SMDS_MeshNode* n2 = edge->GetNode(1);
9372
9373         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9374
9375         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9376         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9377       }
9378       else
9379       {
9380         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9381       }
9382     }
9383
9384     // convert faces
9385     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9386     while( aFaceItr->more() )
9387     {
9388       const SMDS_MeshFace* face = aFaceItr->next();
9389       if ( !face ) continue;
9390       
9391       const SMDSAbs_EntityType type = face->GetEntityType();
9392       bool alreadyOK;
9393       switch( type )
9394       {
9395       case SMDSEntity_Quad_Triangle:
9396       case SMDSEntity_Quad_Quadrangle:
9397         alreadyOK = !theToBiQuad;
9398         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9399         break;
9400       case SMDSEntity_BiQuad_Triangle:
9401       case SMDSEntity_BiQuad_Quadrangle:
9402         alreadyOK = theToBiQuad;
9403         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9404         break;
9405       default: alreadyOK = false;
9406       }
9407       if ( alreadyOK )
9408         continue;
9409
9410       const smIdType id = face->GetID();
9411       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9412
9413       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9414
9415       SMDS_MeshFace * NewFace = 0;
9416       switch( type )
9417       {
9418       case SMDSEntity_Triangle:
9419       case SMDSEntity_Quad_Triangle:
9420       case SMDSEntity_BiQuad_Triangle:
9421         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9422         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9423           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9424         break;
9425
9426       case SMDSEntity_Quadrangle:
9427       case SMDSEntity_Quad_Quadrangle:
9428       case SMDSEntity_BiQuad_Quadrangle:
9429         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9430         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9431           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9432         break;
9433
9434       default:;
9435         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9436       }
9437       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9438     }
9439
9440     // convert volumes
9441     vector<int> nbNodeInFaces;
9442     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9443     while(aVolumeItr->more())
9444     {
9445       const SMDS_MeshVolume* volume = aVolumeItr->next();
9446       if ( !volume ) continue;
9447
9448       const SMDSAbs_EntityType type = volume->GetEntityType();
9449       if ( volume->IsQuadratic() )
9450       {
9451         bool alreadyOK;
9452         switch ( type )
9453         {
9454         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9455         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9456         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9457         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9458         default:                      alreadyOK = true;
9459         }
9460         if ( alreadyOK )
9461         {
9462           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9463           continue;
9464         }
9465       }
9466       const smIdType id = volume->GetID();
9467       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9468       if ( type == SMDSEntity_Polyhedra )
9469         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9470       else if ( type == SMDSEntity_Hexagonal_Prism )
9471         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9472
9473       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9474
9475       SMDS_MeshVolume * NewVolume = 0;
9476       switch ( type )
9477       {
9478       case SMDSEntity_Tetra:
9479         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9480         break;
9481       case SMDSEntity_Hexa:
9482       case SMDSEntity_Quad_Hexa:
9483       case SMDSEntity_TriQuad_Hexa:
9484         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9485                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9486         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9487           if ( nodes[i]->NbInverseElements() == 0 )
9488             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9489         break;
9490       case SMDSEntity_Pyramid:
9491         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9492                                       nodes[3], nodes[4], id, theForce3d);
9493         break;
9494       case SMDSEntity_Penta:
9495       case SMDSEntity_Quad_Penta:
9496       case SMDSEntity_BiQuad_Penta:
9497         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9498                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9499         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9500           if ( nodes[i]->NbInverseElements() == 0 )
9501             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9502         break;
9503       case SMDSEntity_Hexagonal_Prism:
9504       default:
9505         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9506       }
9507       ReplaceElemInGroups(volume, NewVolume, meshDS);
9508     }
9509   }
9510
9511   if ( !theForce3d )
9512   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9513     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9514     // aHelper.FixQuadraticElements(myError);
9515     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9516   }
9517 }
9518
9519 //================================================================================
9520 /*!
9521  * \brief Makes given elements quadratic
9522  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9523  *  \param theElements - elements to make quadratic
9524  */
9525 //================================================================================
9526
9527 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9528                                           TIDSortedElemSet& theElements,
9529                                           const bool        theToBiQuad)
9530 {
9531   if ( theElements.empty() ) return;
9532
9533   // we believe that all theElements are of the same type
9534   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9535
9536   // get all nodes shared by theElements
9537   TIDSortedNodeSet allNodes;
9538   TIDSortedElemSet::iterator eIt = theElements.begin();
9539   for ( ; eIt != theElements.end(); ++eIt )
9540     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9541
9542   // complete theElements with elements of lower dim whose all nodes are in allNodes
9543
9544   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9545   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9546   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9547   for ( ; nIt != allNodes.end(); ++nIt )
9548   {
9549     const SMDS_MeshNode* n = *nIt;
9550     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9551     while ( invIt->more() )
9552     {
9553       const SMDS_MeshElement*      e = invIt->next();
9554       const SMDSAbs_ElementType type = e->GetType();
9555       if ( e->IsQuadratic() )
9556       {
9557         quadAdjacentElems[ type ].insert( e );
9558
9559         bool alreadyOK;
9560         switch ( e->GetEntityType() ) {
9561         case SMDSEntity_Quad_Triangle:
9562         case SMDSEntity_Quad_Quadrangle:
9563         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9564         case SMDSEntity_BiQuad_Triangle:
9565         case SMDSEntity_BiQuad_Quadrangle:
9566         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9567         default:                           alreadyOK = true;
9568         }
9569         if ( alreadyOK )
9570           continue;
9571       }
9572       if ( type >= elemType )
9573         continue; // same type or more complex linear element
9574
9575       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9576         continue; // e is already checked
9577
9578       // check nodes
9579       bool allIn = true;
9580       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9581       while ( nodeIt->more() && allIn )
9582         allIn = allNodes.count( nodeIt->next() );
9583       if ( allIn )
9584         theElements.insert(e );
9585     }
9586   }
9587
9588   SMESH_MesherHelper helper(*myMesh);
9589   helper.SetIsQuadratic( true );
9590   helper.SetIsBiQuadratic( theToBiQuad );
9591
9592   // add links of quadratic adjacent elements to the helper
9593
9594   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9595     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9596           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9597     {
9598       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9599     }
9600   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9601     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9602           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9603     {
9604       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9605     }
9606   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9607     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9608           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9609     {
9610       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9611     }
9612
9613   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9614
9615   SMESHDS_Mesh*  meshDS = GetMeshDS();
9616   SMESHDS_SubMesh* smDS = 0;
9617   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9618   {
9619     const SMDS_MeshElement* elem = *eIt;
9620
9621     bool alreadyOK;
9622     int nbCentralNodes = 0;
9623     switch ( elem->GetEntityType() ) {
9624       // linear convertible
9625     case SMDSEntity_Edge:
9626     case SMDSEntity_Triangle:
9627     case SMDSEntity_Quadrangle:
9628     case SMDSEntity_Tetra:
9629     case SMDSEntity_Pyramid:
9630     case SMDSEntity_Hexa:
9631     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9632       // quadratic that can become bi-quadratic
9633     case SMDSEntity_Quad_Triangle:
9634     case SMDSEntity_Quad_Quadrangle:
9635     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9636       // bi-quadratic
9637     case SMDSEntity_BiQuad_Triangle:
9638     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9639     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9640       // the rest
9641     default:                           alreadyOK = true;
9642     }
9643     if ( alreadyOK ) continue;
9644
9645     const SMDSAbs_ElementType type = elem->GetType();
9646     const smIdType              id = elem->GetID();
9647     const int              nbNodes = elem->NbCornerNodes();
9648     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9649
9650     helper.SetSubShape( elem->getshapeId() );
9651
9652     if ( !smDS || !smDS->Contains( elem ))
9653       smDS = meshDS->MeshElements( elem->getshapeId() );
9654     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9655
9656     SMDS_MeshElement * newElem = 0;
9657     switch( nbNodes )
9658     {
9659     case 4: // cases for most frequently used element types go first (for optimization)
9660       if ( type == SMDSAbs_Volume )
9661         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9662       else
9663         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9664       break;
9665     case 8:
9666       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9668       break;
9669     case 3:
9670       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9671       break;
9672     case 2:
9673       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9674       break;
9675     case 5:
9676       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9677                                  nodes[4], id, theForce3d);
9678       break;
9679     case 6:
9680       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9681                                  nodes[4], nodes[5], id, theForce3d);
9682       break;
9683     default:;
9684     }
9685     ReplaceElemInGroups( elem, newElem, meshDS);
9686     if( newElem && smDS )
9687       smDS->AddElement( newElem );
9688
9689     // remove central nodes
9690     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9691       if ( nodes[i]->NbInverseElements() == 0 )
9692         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9693
9694   } // loop on theElements
9695
9696   if ( !theForce3d )
9697   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9698     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9699     // helper.FixQuadraticElements( myError );
9700     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9701   }
9702 }
9703
9704 //=======================================================================
9705 /*!
9706  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9707  * \return smIdType - nb of checked elements
9708  */
9709 //=======================================================================
9710
9711 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9712                                           SMDS_ElemIteratorPtr theItr,
9713                                           const int            /*theShapeID*/)
9714 {
9715   smIdType nbElem = 0;
9716   SMESHDS_Mesh* meshDS = GetMeshDS();
9717   ElemFeatures elemType;
9718   vector<const SMDS_MeshNode *> nodes;
9719
9720   while( theItr->more() )
9721   {
9722     const SMDS_MeshElement* elem = theItr->next();
9723     nbElem++;
9724     if( elem && elem->IsQuadratic())
9725     {
9726       // get elem data
9727       int nbCornerNodes = elem->NbCornerNodes();
9728       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9729
9730       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9731
9732       //remove a quadratic element
9733       if ( !theSm || !theSm->Contains( elem ))
9734         theSm = meshDS->MeshElements( elem->getshapeId() );
9735       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9736
9737       // remove medium nodes
9738       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9739         if ( nodes[i]->NbInverseElements() == 0 )
9740           meshDS->RemoveFreeNode( nodes[i], theSm );
9741
9742       // add a linear element
9743       nodes.resize( nbCornerNodes );
9744       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9745       ReplaceElemInGroups(elem, newElem, meshDS);
9746       if( theSm && newElem )
9747         theSm->AddElement( newElem );
9748     }
9749   }
9750   return nbElem;
9751 }
9752
9753 //=======================================================================
9754 //function : ConvertFromQuadratic
9755 //purpose  :
9756 //=======================================================================
9757
9758 bool SMESH_MeshEditor::ConvertFromQuadratic()
9759 {
9760   smIdType nbCheckedElems = 0;
9761   if ( myMesh->HasShapeToMesh() )
9762   {
9763     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9764     {
9765       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9766       while ( smIt->more() ) {
9767         SMESH_subMesh* sm = smIt->next();
9768         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9769           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9770       }
9771     }
9772   }
9773
9774   smIdType totalNbElems =
9775     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9776   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9777   {
9778     SMESHDS_SubMesh *aSM = 0;
9779     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9780   }
9781
9782   return true;
9783 }
9784
9785 namespace
9786 {
9787   //================================================================================
9788   /*!
9789    * \brief Return true if all medium nodes of the element are in the node set
9790    */
9791   //================================================================================
9792
9793   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9794   {
9795     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9796       if ( !nodeSet.count( elem->GetNode(i) ))
9797         return false;
9798     return true;
9799   }
9800 }
9801
9802 //================================================================================
9803 /*!
9804  * \brief Makes given elements linear
9805  */
9806 //================================================================================
9807
9808 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9809 {
9810   if ( theElements.empty() ) return;
9811
9812   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9813   set<smIdType> mediumNodeIDs;
9814   TIDSortedElemSet::iterator eIt = theElements.begin();
9815   for ( ; eIt != theElements.end(); ++eIt )
9816   {
9817     const SMDS_MeshElement* e = *eIt;
9818     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9819       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9820   }
9821
9822   // replace given elements by linear ones
9823   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9824   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9825
9826   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9827   // except those elements sharing medium nodes of quadratic element whose medium nodes
9828   // are not all in mediumNodeIDs
9829
9830   // get remaining medium nodes
9831   TIDSortedNodeSet mediumNodes;
9832   set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9833   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9834     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9835       mediumNodes.insert( mediumNodes.end(), n );
9836
9837   // find more quadratic elements to convert
9838   TIDSortedElemSet moreElemsToConvert;
9839   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9840   for ( ; nIt != mediumNodes.end(); ++nIt )
9841   {
9842     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9843     while ( invIt->more() )
9844     {
9845       const SMDS_MeshElement* e = invIt->next();
9846       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9847       {
9848         // find a more complex element including e and
9849         // whose medium nodes are not in mediumNodes
9850         bool complexFound = false;
9851         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9852         {
9853           SMDS_ElemIteratorPtr invIt2 =
9854             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9855           while ( invIt2->more() )
9856           {
9857             const SMDS_MeshElement* eComplex = invIt2->next();
9858             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9859             {
9860               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9861               if ( nbCommonNodes == e->NbNodes())
9862               {
9863                 complexFound = true;
9864                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9865                 break;
9866               }
9867             }
9868           }
9869         }
9870         if ( !complexFound )
9871           moreElemsToConvert.insert( e );
9872       }
9873     }
9874   }
9875   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9876   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9877 }
9878
9879 //=======================================================================
9880 //function : SewSideElements
9881 //purpose  :
9882 //=======================================================================
9883
9884 SMESH_MeshEditor::Sew_Error
9885 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9886                                    TIDSortedElemSet&    theSide2,
9887                                    const SMDS_MeshNode* theFirstNode1,
9888                                    const SMDS_MeshNode* theFirstNode2,
9889                                    const SMDS_MeshNode* theSecondNode1,
9890                                    const SMDS_MeshNode* theSecondNode2)
9891 {
9892   ClearLastCreated();
9893
9894   if ( theSide1.size() != theSide2.size() )
9895     return SEW_DIFF_NB_OF_ELEMENTS;
9896
9897   Sew_Error aResult = SEW_OK;
9898   // Algo:
9899   // 1. Build set of faces representing each side
9900   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9901   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9902
9903   // =======================================================================
9904   // 1. Build set of faces representing each side:
9905   // =======================================================================
9906   // a. build set of nodes belonging to faces
9907   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9908   // c. create temporary faces representing side of volumes if correspondent
9909   //    face does not exist
9910
9911   SMESHDS_Mesh* aMesh = GetMeshDS();
9912   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9913   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9914   TIDSortedElemSet             faceSet1, faceSet2;
9915   set<const SMDS_MeshElement*> volSet1,  volSet2;
9916   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9917   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9918   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9919   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9920   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9921   int iSide, iFace, iNode;
9922
9923   list<const SMDS_MeshElement* > tempFaceList;
9924   for ( iSide = 0; iSide < 2; iSide++ ) {
9925     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9926     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9927     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9928     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9929     set<const SMDS_MeshElement*>::iterator vIt;
9930     TIDSortedElemSet::iterator eIt;
9931     set<const SMDS_MeshNode*>::iterator    nIt;
9932
9933     // check that given nodes belong to given elements
9934     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9935     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9936     int firstIndex = -1, secondIndex = -1;
9937     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9938       const SMDS_MeshElement* elem = *eIt;
9939       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9940       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9941       if ( firstIndex > -1 && secondIndex > -1 ) break;
9942     }
9943     if ( firstIndex < 0 || secondIndex < 0 ) {
9944       // we can simply return until temporary faces created
9945       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9946     }
9947
9948     // -----------------------------------------------------------
9949     // 1a. Collect nodes of existing faces
9950     //     and build set of face nodes in order to detect missing
9951     //     faces corresponding to sides of volumes
9952     // -----------------------------------------------------------
9953
9954     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9955
9956     // loop on the given element of a side
9957     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9958       //const SMDS_MeshElement* elem = *eIt;
9959       const SMDS_MeshElement* elem = *eIt;
9960       if ( elem->GetType() == SMDSAbs_Face ) {
9961         faceSet->insert( elem );
9962         set <const SMDS_MeshNode*> faceNodeSet;
9963         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9964         while ( nodeIt->more() ) {
9965           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966           nodeSet->insert( n );
9967           faceNodeSet.insert( n );
9968         }
9969         setOfFaceNodeSet.insert( faceNodeSet );
9970       }
9971       else if ( elem->GetType() == SMDSAbs_Volume )
9972         volSet->insert( elem );
9973     }
9974     // ------------------------------------------------------------------------------
9975     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9976     // ------------------------------------------------------------------------------
9977
9978     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9979       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9980       while ( fIt->more() ) { // loop on faces sharing a node
9981         const SMDS_MeshElement* f = fIt->next();
9982         if ( faceSet->find( f ) == faceSet->end() ) {
9983           // check if all nodes are in nodeSet and
9984           // complete setOfFaceNodeSet if they are
9985           set <const SMDS_MeshNode*> faceNodeSet;
9986           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9987           bool allInSet = true;
9988           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9989             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9990             if ( nodeSet->find( n ) == nodeSet->end() )
9991               allInSet = false;
9992             else
9993               faceNodeSet.insert( n );
9994           }
9995           if ( allInSet ) {
9996             faceSet->insert( f );
9997             setOfFaceNodeSet.insert( faceNodeSet );
9998           }
9999         }
10000       }
10001     }
10002
10003     // -------------------------------------------------------------------------
10004     // 1c. Create temporary faces representing sides of volumes if correspondent
10005     //     face does not exist
10006     // -------------------------------------------------------------------------
10007
10008     if ( !volSet->empty() ) {
10009       //int nodeSetSize = nodeSet->size();
10010
10011       // loop on given volumes
10012       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10013         SMDS_VolumeTool vol (*vIt);
10014         // loop on volume faces: find free faces
10015         // --------------------------------------
10016         list<const SMDS_MeshElement* > freeFaceList;
10017         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10018           if ( !vol.IsFreeFace( iFace ))
10019             continue;
10020           // check if there is already a face with same nodes in a face set
10021           const SMDS_MeshElement* aFreeFace = 0;
10022           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10023           int nbNodes = vol.NbFaceNodes( iFace );
10024           set <const SMDS_MeshNode*> faceNodeSet;
10025           vol.GetFaceNodes( iFace, faceNodeSet );
10026           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10027           if ( isNewFace ) {
10028             // no such a face is given but it still can exist, check it
10029             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10030             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10031           }
10032           if ( !aFreeFace ) {
10033             // create a temporary face
10034             if ( nbNodes == 3 ) {
10035               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10036               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10037             }
10038             else if ( nbNodes == 4 ) {
10039               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10040               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10041             }
10042             else {
10043               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10044               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10045               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10046             }
10047             if ( aFreeFace )
10048               tempFaceList.push_back( aFreeFace );
10049           }
10050
10051           if ( aFreeFace )
10052             freeFaceList.push_back( aFreeFace );
10053
10054         } // loop on faces of a volume
10055
10056         // choose one of several free faces of a volume
10057         // --------------------------------------------
10058         if ( freeFaceList.size() > 1 ) {
10059           // choose a face having max nb of nodes shared by other elems of a side
10060           int maxNbNodes = -1;
10061           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10062           while ( fIt != freeFaceList.end() ) { // loop on free faces
10063             int nbSharedNodes = 0;
10064             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10065             while ( nodeIt->more() ) { // loop on free face nodes
10066               const SMDS_MeshNode* n =
10067                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10068               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10069               while ( invElemIt->more() ) {
10070                 const SMDS_MeshElement* e = invElemIt->next();
10071                 nbSharedNodes += faceSet->count( e );
10072                 nbSharedNodes += elemSet->count( e );
10073               }
10074             }
10075             if ( nbSharedNodes > maxNbNodes ) {
10076               maxNbNodes = nbSharedNodes;
10077               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10078             }
10079             else if ( nbSharedNodes == maxNbNodes ) {
10080               fIt++;
10081             }
10082             else {
10083               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10084             }
10085           }
10086           if ( freeFaceList.size() > 1 )
10087           {
10088             // could not choose one face, use another way
10089             // choose a face most close to the bary center of the opposite side
10090             gp_XYZ aBC( 0., 0., 0. );
10091             set <const SMDS_MeshNode*> addedNodes;
10092             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10093             eIt = elemSet2->begin();
10094             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10095               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10096               while ( nodeIt->more() ) { // loop on free face nodes
10097                 const SMDS_MeshNode* n =
10098                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10099                 if ( addedNodes.insert( n ).second )
10100                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10101               }
10102             }
10103             aBC /= addedNodes.size();
10104             double minDist = DBL_MAX;
10105             fIt = freeFaceList.begin();
10106             while ( fIt != freeFaceList.end() ) { // loop on free faces
10107               double dist = 0;
10108               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10109               while ( nodeIt->more() ) { // loop on free face nodes
10110                 const SMDS_MeshNode* n =
10111                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10112                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10113                 dist += ( aBC - p ).SquareModulus();
10114               }
10115               if ( dist < minDist ) {
10116                 minDist = dist;
10117                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10118               }
10119               else
10120                 fIt = freeFaceList.erase( fIt++ );
10121             }
10122           }
10123         } // choose one of several free faces of a volume
10124
10125         if ( freeFaceList.size() == 1 ) {
10126           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10127           faceSet->insert( aFreeFace );
10128           // complete a node set with nodes of a found free face
10129           //           for ( iNode = 0; iNode < ; iNode++ )
10130           //             nodeSet->insert( fNodes[ iNode ] );
10131         }
10132
10133       } // loop on volumes of a side
10134
10135       //       // complete a set of faces if new nodes in a nodeSet appeared
10136       //       // ----------------------------------------------------------
10137       //       if ( nodeSetSize != nodeSet->size() ) {
10138       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10139       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10140       //           while ( fIt->more() ) { // loop on faces sharing a node
10141       //             const SMDS_MeshElement* f = fIt->next();
10142       //             if ( faceSet->find( f ) == faceSet->end() ) {
10143       //               // check if all nodes are in nodeSet and
10144       //               // complete setOfFaceNodeSet if they are
10145       //               set <const SMDS_MeshNode*> faceNodeSet;
10146       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10147       //               bool allInSet = true;
10148       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10149       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10151       //                   allInSet = false;
10152       //                 else
10153       //                   faceNodeSet.insert( n );
10154       //               }
10155       //               if ( allInSet ) {
10156       //                 faceSet->insert( f );
10157       //                 setOfFaceNodeSet.insert( faceNodeSet );
10158       //               }
10159       //             }
10160       //           }
10161       //         }
10162       //       }
10163     } // Create temporary faces, if there are volumes given
10164   } // loop on sides
10165
10166   if ( faceSet1.size() != faceSet2.size() ) {
10167     // delete temporary faces: they are in reverseElements of actual nodes
10168     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10169     //    while ( tmpFaceIt->more() )
10170     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10171     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10172     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10173     //      aMesh->RemoveElement(*tmpFaceIt);
10174     MESSAGE("Diff nb of faces");
10175     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10176   }
10177
10178   // ============================================================
10179   // 2. Find nodes to merge:
10180   //              bind a node to remove to a node to put instead
10181   // ============================================================
10182
10183   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10184   if ( theFirstNode1 != theFirstNode2 )
10185     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10186   if ( theSecondNode1 != theSecondNode2 )
10187     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10188
10189   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10190   set< long > linkIdSet; // links to process
10191   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10192
10193   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10194   list< NLink > linkList[2];
10195   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10196   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10197   // loop on links in linkList; find faces by links and append links
10198   // of the found faces to linkList
10199   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10200   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10201   {
10202     NLink link[] = { *linkIt[0], *linkIt[1] };
10203     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10204     if ( !linkIdSet.count( linkID ) )
10205       continue;
10206
10207     // by links, find faces in the face sets,
10208     // and find indices of link nodes in the found faces;
10209     // in a face set, there is only one or no face sharing a link
10210     // ---------------------------------------------------------------
10211
10212     const SMDS_MeshElement* face[] = { 0, 0 };
10213     vector<const SMDS_MeshNode*> fnodes[2];
10214     int iLinkNode[2][2];
10215     TIDSortedElemSet avoidSet;
10216     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10217       const SMDS_MeshNode* n1 = link[iSide].first;
10218       const SMDS_MeshNode* n2 = link[iSide].second;
10219       //cout << "Side " << iSide << " ";
10220       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10221       // find a face by two link nodes
10222       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10223                                                       *faceSetPtr[ iSide ], avoidSet,
10224                                                       &iLinkNode[iSide][0],
10225                                                       &iLinkNode[iSide][1] );
10226       if ( face[ iSide ])
10227       {
10228         //cout << " F " << face[ iSide]->GetID() <<endl;
10229         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10230         // put face nodes to fnodes
10231         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10232         fnodes[ iSide ].assign( nIt, nEnd );
10233         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10234       }
10235     }
10236
10237     // check similarity of elements of the sides
10238     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10239       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10240       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10241         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10242       }
10243       else {
10244         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10245       }
10246       break; // do not return because it's necessary to remove tmp faces
10247     }
10248
10249     // set nodes to merge
10250     // -------------------
10251
10252     if ( face[0] && face[1] )  {
10253       const int nbNodes = face[0]->NbNodes();
10254       if ( nbNodes != face[1]->NbNodes() ) {
10255         MESSAGE("Diff nb of face nodes");
10256         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10257         break; // do not return because it s necessary to remove tmp faces
10258       }
10259       bool reverse[] = { false, false }; // order of nodes in the link
10260       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10261         // analyse link orientation in faces
10262         int i1 = iLinkNode[ iSide ][ 0 ];
10263         int i2 = iLinkNode[ iSide ][ 1 ];
10264         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10265       }
10266       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10267       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10268       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10269       {
10270         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10271                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10272       }
10273
10274       // add other links of the faces to linkList
10275       // -----------------------------------------
10276
10277       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10278         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10279         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10280         if ( !iter_isnew.second ) { // already in a set: no need to process
10281           linkIdSet.erase( iter_isnew.first );
10282         }
10283         else // new in set == encountered for the first time: add
10284         {
10285           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10286           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10287           linkList[0].push_back ( NLink( n1, n2 ));
10288           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10289         }
10290       }
10291     } // 2 faces found
10292
10293     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10294       break;
10295
10296   } // loop on link lists
10297
10298   if ( aResult == SEW_OK &&
10299        ( //linkIt[0] != linkList[0].end() ||
10300         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10301     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10302              " " << (faceSetPtr[1]->empty()));
10303     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10304   }
10305
10306   // ====================================================================
10307   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10308   // ====================================================================
10309
10310   // delete temporary faces
10311   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10312   //  while ( tmpFaceIt->more() )
10313   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10314   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10315   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10316     aMesh->RemoveElement(*tmpFaceIt);
10317
10318   if ( aResult != SEW_OK)
10319     return aResult;
10320
10321   list< smIdType > nodeIDsToRemove;
10322   vector< const SMDS_MeshNode*> nodes;
10323   ElemFeatures elemType;
10324
10325   // loop on nodes replacement map
10326   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10327   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10328     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10329     {
10330       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10331       nodeIDsToRemove.push_back( nToRemove->GetID() );
10332       // loop on elements sharing nToRemove
10333       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10334       while ( invElemIt->more() ) {
10335         const SMDS_MeshElement* e = invElemIt->next();
10336         // get a new suite of nodes: make replacement
10337         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10338         nodes.resize( nbNodes );
10339         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10340         while ( nIt->more() ) {
10341           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10342           nnIt = nReplaceMap.find( n );
10343           if ( nnIt != nReplaceMap.end() ) {
10344             nbReplaced++;
10345             n = (*nnIt).second;
10346           }
10347           nodes[ i++ ] = n;
10348         }
10349         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10350         //         elemIDsToRemove.push_back( e->GetID() );
10351         //       else
10352         if ( nbReplaced )
10353         {
10354           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10355           aMesh->RemoveElement( e );
10356
10357           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10358           {
10359             AddToSameGroups( newElem, e, aMesh );
10360             if ( int aShapeId = e->getshapeId() )
10361               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10362           }
10363         }
10364       }
10365     }
10366
10367   Remove( nodeIDsToRemove, true );
10368
10369   return aResult;
10370 }
10371
10372 //================================================================================
10373 /*!
10374  * \brief Find corresponding nodes in two sets of faces
10375  * \param theSide1 - first face set
10376  * \param theSide2 - second first face
10377  * \param theFirstNode1 - a boundary node of set 1
10378  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10379  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10380  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10381  * \param nReplaceMap - output map of corresponding nodes
10382  * \return bool  - is a success or not
10383  */
10384 //================================================================================
10385
10386 #ifdef _DEBUG_
10387 //#define DEBUG_MATCHING_NODES
10388 #endif
10389
10390 SMESH_MeshEditor::Sew_Error
10391 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10392                                     set<const SMDS_MeshElement*>& theSide2,
10393                                     const SMDS_MeshNode*          theFirstNode1,
10394                                     const SMDS_MeshNode*          theFirstNode2,
10395                                     const SMDS_MeshNode*          theSecondNode1,
10396                                     const SMDS_MeshNode*          theSecondNode2,
10397                                     TNodeNodeMap &                nReplaceMap)
10398 {
10399   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10400
10401   nReplaceMap.clear();
10402   //if ( theFirstNode1 != theFirstNode2 )
10403   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10404   //if ( theSecondNode1 != theSecondNode2 )
10405   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10406
10407   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10408   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10409
10410   list< NLink > linkList[2];
10411   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10412   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10413
10414   // loop on links in linkList; find faces by links and append links
10415   // of the found faces to linkList
10416   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10417   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10418     NLink link[] = { *linkIt[0], *linkIt[1] };
10419     if ( linkSet.find( link[0] ) == linkSet.end() )
10420       continue;
10421
10422     // by links, find faces in the face sets,
10423     // and find indices of link nodes in the found faces;
10424     // in a face set, there is only one or no face sharing a link
10425     // ---------------------------------------------------------------
10426
10427     const SMDS_MeshElement* face[] = { 0, 0 };
10428     list<const SMDS_MeshNode*> notLinkNodes[2];
10429     //bool reverse[] = { false, false }; // order of notLinkNodes
10430     int nbNodes[2];
10431     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10432     {
10433       const SMDS_MeshNode* n1 = link[iSide].first;
10434       const SMDS_MeshNode* n2 = link[iSide].second;
10435       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10436       set< const SMDS_MeshElement* > facesOfNode1;
10437       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10438       {
10439         // during a loop of the first node, we find all faces around n1,
10440         // during a loop of the second node, we find one face sharing both n1 and n2
10441         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10442         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10443         while ( fIt->more() ) { // loop on faces sharing a node
10444           const SMDS_MeshElement* f = fIt->next();
10445           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10446               ! facesOfNode1.insert( f ).second ) // f encounters twice
10447           {
10448             if ( face[ iSide ] ) {
10449               MESSAGE( "2 faces per link " );
10450               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10451             }
10452             face[ iSide ] = f;
10453             faceSet->erase( f );
10454
10455             // get not link nodes
10456             int nbN = f->NbNodes();
10457             if ( f->IsQuadratic() )
10458               nbN /= 2;
10459             nbNodes[ iSide ] = nbN;
10460             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10461             int i1 = f->GetNodeIndex( n1 );
10462             int i2 = f->GetNodeIndex( n2 );
10463             int iEnd = nbN, iBeg = -1, iDelta = 1;
10464             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10465             if ( reverse ) {
10466               std::swap( iEnd, iBeg ); iDelta = -1;
10467             }
10468             int i = i2;
10469             while ( true ) {
10470               i += iDelta;
10471               if ( i == iEnd ) i = iBeg + iDelta;
10472               if ( i == i1 ) break;
10473               nodes.push_back ( f->GetNode( i ) );
10474             }
10475           }
10476         }
10477       }
10478     }
10479     // check similarity of elements of the sides
10480     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10481       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10482       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10483         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10484       }
10485       else {
10486         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10487       }
10488     }
10489
10490     // set nodes to merge
10491     // -------------------
10492
10493     if ( face[0] && face[1] )  {
10494       if ( nbNodes[0] != nbNodes[1] ) {
10495         MESSAGE("Diff nb of face nodes");
10496         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10497       }
10498 #ifdef DEBUG_MATCHING_NODES
10499       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10500                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10501                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10502 #endif
10503       int nbN = nbNodes[0];
10504       {
10505         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10506         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10507         for ( int i = 0 ; i < nbN - 2; ++i ) {
10508 #ifdef DEBUG_MATCHING_NODES
10509           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10510 #endif
10511           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10512         }
10513       }
10514
10515       // add other links of the face 1 to linkList
10516       // -----------------------------------------
10517
10518       const SMDS_MeshElement* f0 = face[0];
10519       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10520       for ( int i = 0; i < nbN; i++ )
10521       {
10522         const SMDS_MeshNode* n2 = f0->GetNode( i );
10523         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10524           linkSet.insert( SMESH_TLink( n1, n2 ));
10525         if ( !iter_isnew.second ) { // already in a set: no need to process
10526           linkSet.erase( iter_isnew.first );
10527         }
10528         else // new in set == encountered for the first time: add
10529         {
10530 #ifdef DEBUG_MATCHING_NODES
10531           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10532                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10533 #endif
10534           linkList[0].push_back ( NLink( n1, n2 ));
10535           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10536         }
10537         n1 = n2;
10538       }
10539     } // 2 faces found
10540   } // loop on link lists
10541
10542   return SEW_OK;
10543 }
10544
10545 namespace // automatically find theAffectedElems for DoubleNodes()
10546 {
10547   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10548
10549   //--------------------------------------------------------------------------------
10550   // Nodes shared by adjacent FissureBorder's.
10551   // 1 node  if FissureBorder separates faces
10552   // 2 nodes if FissureBorder separates volumes
10553   struct SubBorder
10554   {
10555     const SMDS_MeshNode* _nodes[2];
10556     int                  _nbNodes;
10557
10558     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10559     {
10560       _nodes[0] = n1;
10561       _nodes[1] = n2;
10562       _nbNodes = bool( n1 ) + bool( n2 );
10563       if ( _nbNodes == 2 && n1 > n2 )
10564         std::swap( _nodes[0], _nodes[1] );
10565     }
10566     bool operator<( const SubBorder& other ) const
10567     {
10568       for ( int i = 0; i < _nbNodes; ++i )
10569       {
10570         if ( _nodes[i] < other._nodes[i] ) return true;
10571         if ( _nodes[i] > other._nodes[i] ) return false;
10572       }
10573       return false;
10574     }
10575   };
10576
10577   //--------------------------------------------------------------------------------
10578   // Map a SubBorder to all FissureBorder it bounds
10579   struct FissureBorder;
10580   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10581   typedef TBorderLinks::iterator                               TMappedSub;
10582
10583   //--------------------------------------------------------------------------------
10584   /*!
10585    * \brief Element border (volume facet or face edge) at a fissure
10586    */
10587   struct FissureBorder
10588   {
10589     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10590     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10591
10592     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10593     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10594
10595     FissureBorder( FissureBorder && from ) // move constructor
10596     {
10597       std::swap( _nodes,       from._nodes );
10598       std::swap( _sortedNodes, from._sortedNodes );
10599       _elems[0] = from._elems[0];
10600       _elems[1] = from._elems[1];
10601     }
10602
10603     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10604                    std::vector< const SMDS_MeshElement* > & adjElems)
10605       : _nodes( elemToDuplicate->NbCornerNodes() )
10606     {
10607       for ( size_t i = 0; i < _nodes.size(); ++i )
10608         _nodes[i] = elemToDuplicate->GetNode( i );
10609
10610       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10611       findAdjacent( type, adjElems );
10612     }
10613
10614     FissureBorder( const SMDS_MeshNode**                    nodes,
10615                    const size_t                             nbNodes,
10616                    const SMDSAbs_ElementType                adjElemsType,
10617                    std::vector< const SMDS_MeshElement* > & adjElems)
10618       : _nodes( nodes, nodes + nbNodes )
10619     {
10620       findAdjacent( adjElemsType, adjElems );
10621     }
10622
10623     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10624                        std::vector< const SMDS_MeshElement* > & adjElems)
10625     {
10626       _elems[0] = _elems[1] = 0;
10627       adjElems.clear();
10628       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10629         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10630           _elems[i] = adjElems[i];
10631     }
10632
10633     bool operator<( const FissureBorder& other ) const
10634     {
10635       return GetSortedNodes() < other.GetSortedNodes();
10636     }
10637
10638     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10639     {
10640       if ( _sortedNodes.empty() && !_nodes.empty() )
10641       {
10642         FissureBorder* me = const_cast<FissureBorder*>( this );
10643         me->_sortedNodes = me->_nodes;
10644         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10645       }
10646       return _sortedNodes;
10647     }
10648
10649     size_t NbSub() const
10650     {
10651       return _nodes.size();
10652     }
10653
10654     SubBorder Sub(size_t i) const
10655     {
10656       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10657     }
10658
10659     void AddSelfTo( TBorderLinks& borderLinks )
10660     {
10661       _mappedSubs.resize( NbSub() );
10662       for ( size_t i = 0; i < NbSub(); ++i )
10663       {
10664         TBorderLinks::iterator s2b =
10665           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10666         s2b->second.push_back( this );
10667         _mappedSubs[ i ] = s2b;
10668       }
10669     }
10670
10671     void Clear()
10672     {
10673       _nodes.clear();
10674     }
10675
10676     const SMDS_MeshElement* GetMarkedElem() const
10677     {
10678       if ( _nodes.empty() ) return 0; // cleared
10679       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10680       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10681       return 0;
10682     }
10683
10684     gp_XYZ GetNorm() const // normal to the border
10685     {
10686       gp_XYZ norm;
10687       if ( _nodes.size() == 2 )
10688       {
10689         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10690         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10691           avgNorm += norm;
10692         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10693           avgNorm += norm;
10694
10695         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10696         norm = bordDir ^ avgNorm;
10697       }
10698       else
10699       {
10700         SMESH_NodeXYZ p0( _nodes[0] );
10701         SMESH_NodeXYZ p1( _nodes[1] );
10702         SMESH_NodeXYZ p2( _nodes[2] );
10703         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10704       }
10705       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10706         norm.Reverse();
10707
10708       return norm;
10709     }
10710
10711     void ChooseSide() // mark an _elem located at positive side of fissure
10712     {
10713       _elems[0]->setIsMarked( true );
10714       gp_XYZ norm = GetNorm();
10715       double maxX = norm.Coord(1);
10716       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10717       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10718       if ( maxX < 0 )
10719       {
10720         _elems[0]->setIsMarked( false );
10721         if ( _elems[1] )
10722           _elems[1]->setIsMarked( true );
10723       }
10724     }
10725
10726   }; // struct FissureBorder
10727
10728   //--------------------------------------------------------------------------------
10729   /*!
10730    * \brief Classifier of elements at fissure edge
10731    */
10732   class FissureNormal
10733   {
10734     std::vector< gp_XYZ > _normals;
10735     bool                  _bothIn;
10736
10737   public:
10738     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10739     {
10740       _bothIn = false;
10741       _normals.reserve(2);
10742       _normals.push_back( bord.GetNorm() );
10743       if ( _normals.size() == 2 )
10744         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10745     }
10746
10747     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10748     {
10749       bool isIn = false;
10750       switch ( _normals.size() ) {
10751       case 1:
10752       {
10753         isIn = !isOut( n, _normals[0], elem );
10754         break;
10755       }
10756       case 2:
10757       {
10758         bool in1 = !isOut( n, _normals[0], elem );
10759         bool in2 = !isOut( n, _normals[1], elem );
10760         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10761       }
10762       }
10763       return isIn;
10764     }
10765   };
10766
10767   //================================================================================
10768   /*!
10769    * \brief Classify an element by a plane passing through a node
10770    */
10771   //================================================================================
10772
10773   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10774   {
10775     SMESH_NodeXYZ p = n;
10776     double sumDot = 0;
10777     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10778     {
10779       SMESH_NodeXYZ pi = elem->GetNode( i );
10780       sumDot += norm * ( pi - p );
10781     }
10782     return sumDot < -1e-100;
10783   }
10784
10785   //================================================================================
10786   /*!
10787    * \brief Find FissureBorder's by nodes to duplicate
10788    */
10789   //================================================================================
10790
10791   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10792                            std::vector< FissureBorder > & theFissureBorders )
10793   {
10794     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10795     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10796     if ( !n ) return;
10797     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10798     if ( n->NbInverseElements( elemType ) == 0 )
10799     {
10800       elemType = SMDSAbs_Face;
10801       if ( n->NbInverseElements( elemType ) == 0 )
10802         return;
10803     }
10804     // unmark elements touching the fissure
10805     for ( ; nIt != theNodes.end(); ++nIt )
10806       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10807
10808     // loop on elements touching the fissure to get their borders belonging to the fissure
10809     std::set< FissureBorder >              fissureBorders;
10810     std::vector< const SMDS_MeshElement* > adjElems;
10811     std::vector< const SMDS_MeshNode* >    nodes;
10812     SMDS_VolumeTool volTool;
10813     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10814     {
10815       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10816       while ( invIt->more() )
10817       {
10818         const SMDS_MeshElement* eInv = invIt->next();
10819         if ( eInv->isMarked() ) continue;
10820         eInv->setIsMarked( true );
10821
10822         if ( elemType == SMDSAbs_Volume )
10823         {
10824           volTool.Set( eInv );
10825           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10826           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10827           {
10828             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10829             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10830             nodes.clear();
10831             bool allOnFissure = true;
10832             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10833               if (( allOnFissure = theNodes.count( nn[ iN ])))
10834                 nodes.push_back( nn[ iN ]);
10835             if ( allOnFissure )
10836               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10837                                                                elemType, adjElems )));
10838           }
10839         }
10840         else // elemType == SMDSAbs_Face
10841         {
10842           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10843           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10844           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10845           {
10846             nn[1]      = eInv->GetNode( iN );
10847             onFissure1 = theNodes.count( nn[1] );
10848             if ( onFissure0 && onFissure1 )
10849               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10850             nn[0]      = nn[1];
10851             onFissure0 = onFissure1;
10852           }
10853         }
10854       }
10855     }
10856
10857     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10858     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10859     for ( ; bord != fissureBorders.end(); ++bord )
10860     {
10861       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10862     }
10863     return;
10864   } // findFissureBorders()
10865
10866   //================================================================================
10867   /*!
10868    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10869    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10870    *  \param [in] theNodesNot - nodes not to duplicate
10871    *  \param [out] theAffectedElems - the found elements
10872    */
10873   //================================================================================
10874
10875   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10876                           TIDSortedElemSet&       theAffectedElems)
10877   {
10878     if ( theElemsOrNodes.empty() ) return;
10879
10880     // find FissureBorder's
10881
10882     std::vector< FissureBorder >           fissure;
10883     std::vector< const SMDS_MeshElement* > elemsByFacet;
10884
10885     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10886     if ( (*elIt)->GetType() == SMDSAbs_Node )
10887     {
10888       findFissureBorders( theElemsOrNodes, fissure );
10889     }
10890     else
10891     {
10892       fissure.reserve( theElemsOrNodes.size() );
10893       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10894       {
10895         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10896         if ( !fissure.back()._elems[1] )
10897           fissure.pop_back();
10898       }
10899     }
10900     if ( fissure.empty() )
10901       return;
10902
10903     // fill borderLinks
10904
10905     TBorderLinks borderLinks;
10906
10907     for ( size_t i = 0; i < fissure.size(); ++i )
10908     {
10909       fissure[i].AddSelfTo( borderLinks );
10910     }
10911
10912     // get theAffectedElems
10913
10914     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10915     for ( size_t i = 0; i < fissure.size(); ++i )
10916       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10917       {
10918         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10919                                         false, /*markElem=*/true );
10920       }
10921
10922     std::vector<const SMDS_MeshNode *>                 facetNodes;
10923     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10924     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10925
10926     // choose a side of fissure
10927     fissure[0].ChooseSide();
10928     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10929
10930     size_t nbCheckedBorders = 0;
10931     while ( nbCheckedBorders < fissure.size() )
10932     {
10933       // find a FissureBorder to treat
10934       FissureBorder* bord = 0;
10935       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10936         if ( fissure[i].GetMarkedElem() )
10937           bord = & fissure[i];
10938       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10939         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10940         {
10941           bord = & fissure[i];
10942           bord->ChooseSide();
10943           theAffectedElems.insert( bord->GetMarkedElem() );
10944         }
10945       if ( !bord ) return;
10946       ++nbCheckedBorders;
10947
10948       // treat FissureBorder's linked to bord
10949       fissureNodes.clear();
10950       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10951       for ( size_t i = 0; i < bord->NbSub(); ++i )
10952       {
10953         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10954         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10955         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10956         const SubBorder&                          sb = l2b->first;
10957         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10958
10959         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10960         {
10961           for ( int j = 0; j < sb._nbNodes; ++j )
10962             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10963           continue;
10964         }
10965
10966         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10967         // until an elem adjacent to a neighbour FissureBorder is found
10968         facetNodes.clear();
10969         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10970         facetNodes.resize( sb._nbNodes + 1 );
10971
10972         while ( bordElem )
10973         {
10974           // check if bordElem is adjacent to a neighbour FissureBorder
10975           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10976           {
10977             FissureBorder* bord2 = linkedBorders[j];
10978             if ( bord2 == bord ) continue;
10979             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10980               bordElem = 0;
10981             else
10982               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10983           }
10984           if ( !bordElem )
10985             break;
10986
10987           // find the next bordElem
10988           const SMDS_MeshElement* nextBordElem = 0;
10989           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10990           {
10991             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10992             if ( fissureNodes.count( n )) continue;
10993
10994             facetNodes[ sb._nbNodes ] = n;
10995             elemsByFacet.clear();
10996             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10997             {
10998               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10999                 if ( elemsByFacet[ iE ] != bordElem &&
11000                      !elemsByFacet[ iE ]->isMarked() )
11001                 {
11002                   theAffectedElems.insert( elemsByFacet[ iE ]);
11003                   elemsByFacet[ iE ]->setIsMarked( true );
11004                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11005                     nextBordElem = elemsByFacet[ iE ];
11006                 }
11007             }
11008           }
11009           bordElem = nextBordElem;
11010
11011         } // while ( bordElem )
11012
11013         linkedBorders.clear(); // not to treat this link any more
11014
11015       } // loop on SubBorder's of a FissureBorder
11016
11017       bord->Clear();
11018
11019     } // loop on FissureBorder's
11020
11021
11022     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11023
11024     // mark nodes of theAffectedElems
11025     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11026
11027     // unmark nodes of the fissure
11028     elIt = theElemsOrNodes.begin();
11029     if ( (*elIt)->GetType() == SMDSAbs_Node )
11030       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11031     else
11032       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11033
11034     std::vector< gp_XYZ > normVec;
11035
11036     // loop on nodes of the fissure, add elements having marked nodes
11037     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11038     {
11039       const SMDS_MeshElement* e = (*elIt);
11040       if ( e->GetType() != SMDSAbs_Node )
11041         e->setIsMarked( true ); // avoid adding a fissure element
11042
11043       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11044       {
11045         const SMDS_MeshNode* n = e->GetNode( iN );
11046         if ( fissEdgeNodes2Norm.count( n ))
11047           continue;
11048
11049         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11050         while ( invIt->more() )
11051         {
11052           const SMDS_MeshElement* eInv = invIt->next();
11053           if ( eInv->isMarked() ) continue;
11054           eInv->setIsMarked( true );
11055
11056           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11057           while( nIt->more() )
11058             if ( nIt->next()->isMarked())
11059             {
11060               theAffectedElems.insert( eInv );
11061               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11062               n->setIsMarked( false );
11063               break;
11064             }
11065         }
11066       }
11067     }
11068
11069     // add elements on the fissure edge
11070     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11071     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11072     {
11073       const SMDS_MeshNode* edgeNode = n2N->first;
11074       const FissureNormal & normals = n2N->second;
11075
11076       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11077       while ( invIt->more() )
11078       {
11079         const SMDS_MeshElement* eInv = invIt->next();
11080         if ( eInv->isMarked() ) continue;
11081         eInv->setIsMarked( true );
11082
11083         // classify eInv using normals
11084         bool toAdd = normals.IsIn( edgeNode, eInv );
11085         if ( toAdd ) // check if all nodes lie on the fissure edge
11086         {
11087           bool notOnEdge = false;
11088           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
11089             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11090           toAdd = notOnEdge;
11091         }
11092         if ( toAdd )
11093         {
11094           theAffectedElems.insert( eInv );
11095         }
11096       }
11097     }
11098
11099     return;
11100   } // findAffectedElems()
11101 } // namespace
11102
11103 //================================================================================
11104 /*!
11105  * \brief Create elements equal (on same nodes) to given ones
11106  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
11107  *              elements of the uppest dimension are duplicated.
11108  */
11109 //================================================================================
11110
11111 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11112 {
11113   ClearLastCreated();
11114   SMESHDS_Mesh* mesh = GetMeshDS();
11115
11116   // get an element type and an iterator over elements
11117
11118   SMDSAbs_ElementType type = SMDSAbs_All;
11119   SMDS_ElemIteratorPtr elemIt;
11120   if ( theElements.empty() )
11121   {
11122     if ( mesh->NbNodes() == 0 )
11123       return;
11124     // get most complex type
11125     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11126       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11127       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11128     };
11129     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11130       if ( mesh->GetMeshInfo().NbElements( types[i] ))
11131       {
11132         type = types[i];
11133         elemIt = mesh->elementsIterator( type );
11134         break;
11135       }
11136   }
11137   else
11138   {
11139     //type = (*theElements.begin())->GetType();
11140     elemIt = SMESHUtils::elemSetIterator( theElements );
11141   }
11142
11143   // un-mark all elements to avoid duplicating just created elements
11144   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11145
11146   // duplicate elements
11147
11148   ElemFeatures elemType;
11149
11150   vector< const SMDS_MeshNode* > nodes;
11151   while ( elemIt->more() )
11152   {
11153     const SMDS_MeshElement* elem = elemIt->next();
11154     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11155         ( elem->isMarked() ))
11156       continue;
11157
11158     elemType.Init( elem, /*basicOnly=*/false );
11159     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11160
11161     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11162       newElem->setIsMarked( true );
11163   }
11164 }
11165
11166 //================================================================================
11167 /*!
11168   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11169   \param theElems - the list of elements (edges or faces) to be replicated
11170   The nodes for duplication could be found from these elements
11171   \param theNodesNot - list of nodes to NOT replicate
11172   \param theAffectedElems - the list of elements (cells and edges) to which the
11173   replicated nodes should be associated to.
11174   \return TRUE if operation has been completed successfully, FALSE otherwise
11175 */
11176 //================================================================================
11177
11178 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11179                                     const TIDSortedElemSet& theNodesNot,
11180                                     const TIDSortedElemSet& theAffectedElems )
11181 {
11182   ClearLastCreated();
11183
11184   if ( theElems.size() == 0 )
11185     return false;
11186
11187   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11188   if ( !aMeshDS )
11189     return false;
11190
11191   bool res = false;
11192   TNodeNodeMap anOldNodeToNewNode;
11193   // duplicate elements and nodes
11194   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11195   // replce nodes by duplications
11196   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11197   return res;
11198 }
11199
11200 //================================================================================
11201 /*!
11202   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11203   \param theMeshDS - mesh instance
11204   \param theElems - the elements replicated or modified (nodes should be changed)
11205   \param theNodesNot - nodes to NOT replicate
11206   \param theNodeNodeMap - relation of old node to new created node
11207   \param theIsDoubleElem - flag os to replicate element or modify
11208   \return TRUE if operation has been completed successfully, FALSE otherwise
11209 */
11210 //================================================================================
11211
11212 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11213                                    const TIDSortedElemSet& theElems,
11214                                    const TIDSortedElemSet& theNodesNot,
11215                                    TNodeNodeMap&           theNodeNodeMap,
11216                                    const bool              theIsDoubleElem )
11217 {
11218   // iterate through element and duplicate them (by nodes duplication)
11219   bool res = false;
11220   std::vector<const SMDS_MeshNode*> newNodes;
11221   ElemFeatures elemType;
11222
11223   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11224   for ( ;  elemItr != theElems.end(); ++elemItr )
11225   {
11226     const SMDS_MeshElement* anElem = *elemItr;
11227     // if (!anElem)
11228     //   continue;
11229
11230     // duplicate nodes to duplicate element
11231     bool isDuplicate = false;
11232     newNodes.resize( anElem->NbNodes() );
11233     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11234     int ind = 0;
11235     while ( anIter->more() )
11236     {
11237       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11238       const SMDS_MeshNode*  aNewNode = aCurrNode;
11239       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11240       if ( n2n != theNodeNodeMap.end() )
11241       {
11242         aNewNode = n2n->second;
11243       }
11244       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11245       {
11246         // duplicate node
11247         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11248         copyPosition( aCurrNode, aNewNode );
11249         theNodeNodeMap[ aCurrNode ] = aNewNode;
11250         myLastCreatedNodes.push_back( aNewNode );
11251       }
11252       isDuplicate |= (aCurrNode != aNewNode);
11253       newNodes[ ind++ ] = aNewNode;
11254     }
11255     if ( !isDuplicate )
11256       continue;
11257
11258     if ( theIsDoubleElem )
11259       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11260     else
11261     {
11262       // change element nodes
11263       const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11264       if ( geomType == SMDSEntity_Polyhedra )
11265       {
11266         // special treatment for polyhedron
11267         const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11268         if (!aPolyhedron) {
11269           MESSAGE("Warning: bad volumic element");
11270           return false;
11271         }
11272         theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11273       }
11274       else
11275         // standard entity type
11276         theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11277     }
11278
11279     res = true;
11280   }
11281   return res;
11282 }
11283
11284 //================================================================================
11285 /*!
11286   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11287   \param theNodes - identifiers of nodes to be doubled
11288   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11289   nodes. If list of element identifiers is empty then nodes are doubled but
11290   they not assigned to elements
11291   \return TRUE if operation has been completed successfully, FALSE otherwise
11292 */
11293 //================================================================================
11294
11295 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11296                                     const std::list< int >& theListOfModifiedElems )
11297 {
11298   ClearLastCreated();
11299
11300   if ( theListOfNodes.size() == 0 )
11301     return false;
11302
11303   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11304   if ( !aMeshDS )
11305     return false;
11306
11307   // iterate through nodes and duplicate them
11308
11309   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11310
11311   std::list< int >::const_iterator aNodeIter;
11312   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11313   {
11314     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11315     if ( !aNode )
11316       continue;
11317
11318     // duplicate node
11319
11320     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11321     if ( aNewNode )
11322     {
11323       copyPosition( aNode, aNewNode );
11324       anOldNodeToNewNode[ aNode ] = aNewNode;
11325       myLastCreatedNodes.push_back( aNewNode );
11326     }
11327   }
11328
11329   // Change nodes of elements
11330
11331   std::vector<const SMDS_MeshNode*> aNodeArr;
11332
11333   std::list< int >::const_iterator anElemIter;
11334   for ( anElemIter =  theListOfModifiedElems.begin();
11335         anElemIter != theListOfModifiedElems.end();
11336         anElemIter++ )
11337   {
11338     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11339     if ( !anElem )
11340       continue;
11341
11342     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11343     for( size_t i = 0; i < aNodeArr.size(); ++i )
11344     {
11345       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11346         anOldNodeToNewNode.find( aNodeArr[ i ]);
11347       if ( n2n != anOldNodeToNewNode.end() )
11348         aNodeArr[ i ] = n2n->second;
11349     }
11350     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11351   }
11352
11353   return true;
11354 }
11355
11356 namespace {
11357
11358   //================================================================================
11359   /*!
11360     \brief Check if element located inside shape
11361     \return TRUE if IN or ON shape, FALSE otherwise
11362   */
11363   //================================================================================
11364
11365   template<class Classifier>
11366   bool isInside(const SMDS_MeshElement* theElem,
11367                 Classifier&             theClassifier,
11368                 const double            theTol)
11369   {
11370     gp_XYZ centerXYZ (0, 0, 0);
11371     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11372       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11373
11374     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11375     theClassifier.Perform(aPnt, theTol);
11376     TopAbs_State aState = theClassifier.State();
11377     return (aState == TopAbs_IN || aState == TopAbs_ON );
11378   }
11379
11380   //================================================================================
11381   /*!
11382    * \brief Classifier of the 3D point on the TopoDS_Face
11383    *        with interaface suitable for isInside()
11384    */
11385   //================================================================================
11386
11387   struct _FaceClassifier
11388   {
11389     Extrema_ExtPS       _extremum;
11390     BRepAdaptor_Surface _surface;
11391     TopAbs_State        _state;
11392
11393     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11394     {
11395       _extremum.Initialize( _surface,
11396                             _surface.FirstUParameter(), _surface.LastUParameter(),
11397                             _surface.FirstVParameter(), _surface.LastVParameter(),
11398                             _surface.Tolerance(), _surface.Tolerance() );
11399     }
11400     void Perform(const gp_Pnt& aPnt, double theTol)
11401     {
11402       theTol *= theTol;
11403       _state = TopAbs_OUT;
11404       _extremum.Perform(aPnt);
11405       if ( _extremum.IsDone() )
11406         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11407           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11408     }
11409     TopAbs_State State() const
11410     {
11411       return _state;
11412     }
11413   };
11414 }
11415
11416 //================================================================================
11417 /*!
11418   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11419   This method is the first step of DoubleNodeElemGroupsInRegion.
11420   \param theElems - list of groups of elements (edges or faces) to be replicated
11421   \param theNodesNot - list of groups of nodes not to replicate
11422   \param theShape - shape to detect affected elements (element which geometric center
11423          located on or inside shape). If the shape is null, detection is done on faces orientations
11424          (select elements with a gravity center on the side given by faces normals).
11425          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11426          The replicated nodes should be associated to affected elements.
11427   \return true
11428   \sa DoubleNodeElemGroupsInRegion()
11429 */
11430 //================================================================================
11431
11432 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11433                                                    const TIDSortedElemSet& theNodesNot,
11434                                                    const TopoDS_Shape&     theShape,
11435                                                    TIDSortedElemSet&       theAffectedElems)
11436 {
11437   if ( theShape.IsNull() )
11438   {
11439     findAffectedElems( theElems, theAffectedElems );
11440   }
11441   else
11442   {
11443     const double aTol = Precision::Confusion();
11444     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11445     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11446     if ( theShape.ShapeType() == TopAbs_SOLID )
11447     {
11448       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11449       bsc3d->PerformInfinitePoint(aTol);
11450     }
11451     else if (theShape.ShapeType() == TopAbs_FACE )
11452     {
11453       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11454     }
11455
11456     // iterates on indicated elements and get elements by back references from their nodes
11457     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11458     for ( ;  elemItr != theElems.end(); ++elemItr )
11459     {
11460       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11461       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11462       while ( nodeItr->more() )
11463       {
11464         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11465         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11466           continue;
11467         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11468         while ( backElemItr->more() )
11469         {
11470           const SMDS_MeshElement* curElem = backElemItr->next();
11471           if ( curElem && theElems.find(curElem) == theElems.end() &&
11472                ( bsc3d.get() ?
11473                  isInside( curElem, *bsc3d, aTol ) :
11474                  isInside( curElem, *aFaceClassifier, aTol )))
11475             theAffectedElems.insert( curElem );
11476         }
11477       }
11478     }
11479   }
11480   return true;
11481 }
11482
11483 //================================================================================
11484 /*!
11485   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11486   \param theElems - group of of elements (edges or faces) to be replicated
11487   \param theNodesNot - group of nodes not to replicate
11488   \param theShape - shape to detect affected elements (element which geometric center
11489   located on or inside shape).
11490   The replicated nodes should be associated to affected elements.
11491   \return TRUE if operation has been completed successfully, FALSE otherwise
11492 */
11493 //================================================================================
11494
11495 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11496                                             const TIDSortedElemSet& theNodesNot,
11497                                             const TopoDS_Shape&     theShape )
11498 {
11499   if ( theShape.IsNull() )
11500     return false;
11501
11502   const double aTol = Precision::Confusion();
11503   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11504   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11505   if ( theShape.ShapeType() == TopAbs_SOLID )
11506   {
11507     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11508     bsc3d->PerformInfinitePoint(aTol);
11509   }
11510   else if (theShape.ShapeType() == TopAbs_FACE )
11511   {
11512     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11513   }
11514
11515   // iterates on indicated elements and get elements by back references from their nodes
11516   TIDSortedElemSet anAffected;
11517   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11518   for ( ;  elemItr != theElems.end(); ++elemItr )
11519   {
11520     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11521     if (!anElem)
11522       continue;
11523
11524     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11525     while ( nodeItr->more() )
11526     {
11527       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11528       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11529         continue;
11530       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11531       while ( backElemItr->more() )
11532       {
11533         const SMDS_MeshElement* curElem = backElemItr->next();
11534         if ( curElem && theElems.find(curElem) == theElems.end() &&
11535              ( bsc3d ?
11536                isInside( curElem, *bsc3d, aTol ) :
11537                isInside( curElem, *aFaceClassifier, aTol )))
11538           anAffected.insert( curElem );
11539       }
11540     }
11541   }
11542   return DoubleNodes( theElems, theNodesNot, anAffected );
11543 }
11544
11545 /*!
11546  *  \brief compute an oriented angle between two planes defined by four points.
11547  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11548  *  @param p0 base of the rotation axe
11549  *  @param p1 extremity of the rotation axe
11550  *  @param g1 belongs to the first plane
11551  *  @param g2 belongs to the second plane
11552  */
11553 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11554 {
11555   gp_Vec vref(p0, p1);
11556   gp_Vec v1(p0, g1);
11557   gp_Vec v2(p0, g2);
11558   gp_Vec n1 = vref.Crossed(v1);
11559   gp_Vec n2 = vref.Crossed(v2);
11560   try {
11561     return n2.AngleWithRef(n1, vref);
11562   }
11563   catch ( Standard_Failure& ) {
11564   }
11565   return Max( v1.Magnitude(), v2.Magnitude() );
11566 }
11567
11568 /*!
11569  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11570  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11571  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11572  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11573  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11574  * 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.
11575  * 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.
11576  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11577  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11578  * \param theElems - list of groups of volumes, where a group of volume is a set of
11579  *        SMDS_MeshElements sorted by Id.
11580  * \param createJointElems - if TRUE, create the elements
11581  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11582  *        the boundary between \a theDomains and the rest mesh
11583  * \return TRUE if operation has been completed successfully, FALSE otherwise
11584  */
11585 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11586                                                      bool                                 createJointElems,
11587                                                      bool                                 onAllBoundaries)
11588 {
11589   // MESSAGE("----------------------------------------------");
11590   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11591   // MESSAGE("----------------------------------------------");
11592
11593   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11594   meshDS->BuildDownWardConnectivity(true);
11595   CHRONO(50);
11596   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11597
11598   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11599   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11600   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11601
11602   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11603   std::map<int,int> celldom; // cell vtkId --> domain
11604   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11605   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11606
11607   //MESSAGE(".. Number of domains :"<<theElems.size());
11608
11609   TIDSortedElemSet theRestDomElems;
11610   const int iRestDom  = -1;
11611   const int idom0     = onAllBoundaries ? iRestDom : 0;
11612   const int nbDomains = theElems.size();
11613
11614   // Check if the domains do not share an element
11615   for (int idom = 0; idom < nbDomains-1; idom++)
11616   {
11617     //       MESSAGE("... Check of domain #" << idom);
11618     const TIDSortedElemSet& domain = theElems[idom];
11619     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11620     for (; elemItr != domain.end(); ++elemItr)
11621     {
11622       const SMDS_MeshElement* anElem = *elemItr;
11623       int idombisdeb = idom + 1 ;
11624       // check if the element belongs to a domain further in the list
11625       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11626       {
11627         const TIDSortedElemSet& domainbis = theElems[idombis];
11628         if ( domainbis.count( anElem ))
11629         {
11630           MESSAGE(".... Domain #" << idom);
11631           MESSAGE(".... Domain #" << idombis);
11632           throw SALOME_Exception("The domains are not disjoint.");
11633           return false ;
11634         }
11635       }
11636     }
11637   }
11638
11639   for (int idom = 0; idom < nbDomains; idom++)
11640   {
11641
11642     // --- build a map (face to duplicate --> volume to modify)
11643     //     with all the faces shared by 2 domains (group of elements)
11644     //     and corresponding volume of this domain, for each shared face.
11645     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11646
11647     //MESSAGE("... Neighbors of domain #" << idom);
11648     const TIDSortedElemSet& domain = theElems[idom];
11649     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11650     for (; elemItr != domain.end(); ++elemItr)
11651     {
11652       const SMDS_MeshElement* anElem = *elemItr;
11653       if (!anElem)
11654         continue;
11655       vtkIdType vtkId = anElem->GetVtkID();
11656       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11657       int neighborsVtkIds[NBMAXNEIGHBORS];
11658       int downIds[NBMAXNEIGHBORS];
11659       unsigned char downTypes[NBMAXNEIGHBORS];
11660       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11661       for (int n = 0; n < nbNeighbors; n++)
11662       {
11663         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11664         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11665         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11666         {
11667           bool ok = false;
11668           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11669           {
11670             // MESSAGE("Domain " << idombis);
11671             const TIDSortedElemSet& domainbis = theElems[idombis];
11672             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11673           }
11674           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11675           {
11676             DownIdType face(downIds[n], downTypes[n]);
11677             if (!faceDomains[face].count(idom))
11678             {
11679               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11680               celldom[vtkId] = idom;
11681               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11682             }
11683             if ( !ok )
11684             {
11685               theRestDomElems.insert( elem );
11686               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11687               celldom[neighborsVtkIds[n]] = iRestDom;
11688             }
11689           }
11690         }
11691       }
11692     }
11693   }
11694
11695   //MESSAGE("Number of shared faces " << faceDomains.size());
11696   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11697
11698   // --- explore the shared faces domain by domain,
11699   //     explore the nodes of the face and see if they belong to a cell in the domain,
11700   //     which has only a node or an edge on the border (not a shared face)
11701
11702   for (int idomain = idom0; idomain < nbDomains; idomain++)
11703   {
11704     //MESSAGE("Domain " << idomain);
11705     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11706     itface = faceDomains.begin();
11707     for (; itface != faceDomains.end(); ++itface)
11708     {
11709       const std::map<int, int>& domvol = itface->second;
11710       if (!domvol.count(idomain))
11711         continue;
11712       DownIdType face = itface->first;
11713       //MESSAGE(" --- face " << face.cellId);
11714       std::set<int> oldNodes;
11715       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11716       std::set<int>::iterator itn = oldNodes.begin();
11717       for (; itn != oldNodes.end(); ++itn)
11718       {
11719         int oldId = *itn;
11720         //MESSAGE("     node " << oldId);
11721         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11722         for (int i=0; i<l.ncells; i++)
11723         {
11724           int vtkId = l.cells[i];
11725           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11726           if (!domain.count(anElem))
11727             continue;
11728           int vtkType = grid->GetCellType(vtkId);
11729           int downId = grid->CellIdToDownId(vtkId);
11730           if (downId < 0)
11731           {
11732             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11733             continue; // not OK at this stage of the algorithm:
11734             //no cells created after BuildDownWardConnectivity
11735           }
11736           DownIdType aCell(downId, vtkType);
11737           cellDomains[aCell][idomain] = vtkId;
11738           celldom[vtkId] = idomain;
11739           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11740         }
11741       }
11742     }
11743   }
11744
11745   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11746   //     for each shared face, get the nodes
11747   //     for each node, for each domain of the face, create a clone of the node
11748
11749   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11750   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11751   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11752
11753   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11754   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11755   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11756
11757   //MESSAGE(".. Duplication of the nodes");
11758   for (int idomain = idom0; idomain < nbDomains; idomain++)
11759   {
11760     itface = faceDomains.begin();
11761     for (; itface != faceDomains.end(); ++itface)
11762     {
11763       const std::map<int, int>& domvol = itface->second;
11764       if (!domvol.count(idomain))
11765         continue;
11766       DownIdType face = itface->first;
11767       //MESSAGE(" --- face " << face.cellId);
11768       std::set<int> oldNodes;
11769       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11770       std::set<int>::iterator itn = oldNodes.begin();
11771       for (; itn != oldNodes.end(); ++itn)
11772       {
11773         int oldId = *itn;
11774         if (nodeDomains[oldId].empty())
11775         {
11776           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11777           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11778         }
11779         std::map<int, int>::const_iterator itdom = domvol.begin();
11780         for (; itdom != domvol.end(); ++itdom)
11781         {
11782           int idom = itdom->first;
11783           //MESSAGE("         domain " << idom);
11784           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11785           {
11786             if (nodeDomains[oldId].size() >= 2) // a multiple node
11787             {
11788               vector<int> orderedDoms;
11789               //MESSAGE("multiple node " << oldId);
11790               if (mutipleNodes.count(oldId))
11791                 orderedDoms = mutipleNodes[oldId];
11792               else
11793               {
11794                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11795                 for (; it != nodeDomains[oldId].end(); ++it)
11796                   orderedDoms.push_back(it->first);
11797               }
11798               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11799               //stringstream txt;
11800               //for (int i=0; i<orderedDoms.size(); i++)
11801               //  txt << orderedDoms[i] << " ";
11802               //MESSAGE("orderedDoms " << txt.str());
11803               mutipleNodes[oldId] = orderedDoms;
11804             }
11805             double *coords = grid->GetPoint(oldId);
11806             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11807             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11808             int newId = newNode->GetVtkID();
11809             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11810             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11811           }
11812         }
11813       }
11814     }
11815   }
11816
11817   //MESSAGE(".. Creation of elements");
11818   for (int idomain = idom0; idomain < nbDomains; idomain++)
11819   {
11820     itface = faceDomains.begin();
11821     for (; itface != faceDomains.end(); ++itface)
11822     {
11823       std::map<int, int> domvol = itface->second;
11824       if (!domvol.count(idomain))
11825         continue;
11826       DownIdType face = itface->first;
11827       //MESSAGE(" --- face " << face.cellId);
11828       std::set<int> oldNodes;
11829       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11830       int nbMultipleNodes = 0;
11831       std::set<int>::iterator itn = oldNodes.begin();
11832       for (; itn != oldNodes.end(); ++itn)
11833       {
11834         int oldId = *itn;
11835         if (mutipleNodes.count(oldId))
11836           nbMultipleNodes++;
11837       }
11838       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11839       {
11840         //MESSAGE("multiple Nodes detected on a shared face");
11841         int downId = itface->first.cellId;
11842         unsigned char cellType = itface->first.cellType;
11843         // --- shared edge or shared face ?
11844         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11845         {
11846           int nodes[3];
11847           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11848           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11849             if (mutipleNodes.count(nodes[i]))
11850               if (!mutipleNodesToFace.count(nodes[i]))
11851                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11852         }
11853         else // shared face (between two volumes)
11854         {
11855           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11856           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11857           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11858           for (int ie =0; ie < nbEdges; ie++)
11859           {
11860             int nodes[3];
11861             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11862             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11863             {
11864               vector<int> vn0 = mutipleNodes[nodes[0]];
11865               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11866               vector<int> doms;
11867               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11868                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11869                   if ( vn0[i0] == vn1[i1] )
11870                     doms.push_back( vn0[ i0 ]);
11871               if ( doms.size() > 2 )
11872               {
11873                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11874                 double *coords = grid->GetPoint(nodes[0]);
11875                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11876                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11877                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11878                 gp_Pnt gref;
11879                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11880                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11881                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11882                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11883                 for ( size_t id = 0; id < doms.size(); id++ )
11884                 {
11885                   int idom = doms[id];
11886                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11887                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11888                   {
11889                     smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11890                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11891                     if (domain.count(elem))
11892                     {
11893                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11894                       domvol[idom] = (SMDS_MeshVolume*) svol;
11895                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11896                       double values[3] = { 0,0,0 };
11897                       vtkIdType npts = 0;
11898                       vtkIdType const *pts(nullptr);
11899                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11900                       for ( vtkIdType i = 0; i < npts; ++i )
11901                       {
11902                         double *coords = grid->GetPoint( pts[i] );
11903                         for ( int j = 0; j < 3; ++j )
11904                           values[j] += coords[j] / npts;
11905                       }
11906                       if ( id == 0 )
11907                       {
11908                         gref.SetCoord( values[0], values[1], values[2] );
11909                         angleDom[idom] = 0;
11910                       }
11911                       else
11912                       {
11913                         gp_Pnt g( values[0], values[1], values[2] );
11914                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11915                         //MESSAGE("  angle=" << angleDom[idom]);
11916                       }
11917                       break;
11918                     }
11919                   }
11920                 }
11921                 map<double, int> sortedDom; // sort domains by angle
11922                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11923                   sortedDom[ia->second] = ia->first;
11924                 vector<int> vnodes;
11925                 vector<int> vdom;
11926                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11927                 {
11928                   vdom.push_back(ib->second);
11929                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11930                 }
11931                 for (int ino = 0; ino < nbNodes; ino++)
11932                   vnodes.push_back(nodes[ino]);
11933                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11934               }
11935             }
11936           }
11937         }
11938       }
11939     }
11940   }
11941
11942   // --- iterate on shared faces (volumes to modify, face to extrude)
11943   //     get node id's of the face (id SMDS = id VTK)
11944   //     create flat element with old and new nodes if requested
11945
11946   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11947   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11948
11949   std::map<int, std::map<long,int> > nodeQuadDomains;
11950   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11951
11952   //MESSAGE(".. Creation of elements: simple junction");
11953   if ( createJointElems )
11954   {
11955     string joints2DName = "joints2D";
11956     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11957     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11958     string joints3DName = "joints3D";
11959     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11960     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11961
11962     itface = faceDomains.begin();
11963     for (; itface != faceDomains.end(); ++itface)
11964     {
11965       DownIdType face = itface->first;
11966       std::set<int> oldNodes;
11967       std::set<int>::iterator itn;
11968       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11969
11970       std::map<int, int>          domvol = itface->second;
11971       std::map<int, int>::iterator itdom = domvol.begin();
11972       int     dom1 = itdom->first;
11973       int vtkVolId = itdom->second;
11974       itdom++;
11975       int           dom2 = itdom->first;
11976       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11977                                                        nodeQuadDomains);
11978       stringstream grpname;
11979       grpname << "j_";
11980       if (dom1 < dom2)
11981         grpname << dom1 << "_" << dom2;
11982       else
11983         grpname << dom2 << "_" << dom1;
11984       string namegrp = grpname.str();
11985       if (!mapOfJunctionGroups.count(namegrp))
11986         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11987       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11988       if (sgrp)
11989         sgrp->Add(vol->GetID());
11990       if (vol->GetType() == SMDSAbs_Volume)
11991         joints3DGrp->Add(vol->GetID());
11992       else if (vol->GetType() == SMDSAbs_Face)
11993         joints2DGrp->Add(vol->GetID());
11994     }
11995   }
11996
11997   // --- create volumes on multiple domain intersection if requested
11998   //     iterate on mutipleNodesToFace
11999   //     iterate on edgesMultiDomains
12000
12001   //MESSAGE(".. Creation of elements: multiple junction");
12002   if (createJointElems)
12003   {
12004     // --- iterate on mutipleNodesToFace
12005
12006     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
12007     for (; itn != mutipleNodesToFace.end(); ++itn)
12008     {
12009       int node = itn->first;
12010       vector<int> orderDom = itn->second;
12011       vector<vtkIdType> orderedNodes;
12012       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12013         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12014       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12015
12016       stringstream grpname;
12017       grpname << "m2j_";
12018       grpname << 0 << "_" << 0;
12019       string namegrp = grpname.str();
12020       if (!mapOfJunctionGroups.count(namegrp))
12021         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
12022       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12023       if (sgrp)
12024         sgrp->Add(face->GetID());
12025     }
12026
12027     // --- iterate on edgesMultiDomains
12028
12029     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12030     for (; ite != edgesMultiDomains.end(); ++ite)
12031     {
12032       vector<int>    nodes = ite->first;
12033       vector<int> orderDom = ite->second;
12034       vector<vtkIdType> orderedNodes;
12035       if (nodes.size() == 2)
12036       {
12037         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12038         for ( size_t ino = 0; ino < nodes.size(); ino++ )
12039           if ( orderDom.size() == 3 )
12040             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12041               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12042           else
12043             for (int idom = orderDom.size()-1; idom >=0; idom--)
12044               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12045         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12046
12047         string namegrp = "jointsMultiples";
12048         if (!mapOfJunctionGroups.count(namegrp))
12049           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12050         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12051         if (sgrp)
12052           sgrp->Add(vol->GetID());
12053       }
12054       else
12055       {
12056         //INFOS("Quadratic multiple joints not implemented");
12057         // TODO quadratic nodes
12058       }
12059     }
12060   }
12061
12062   // --- list the explicit faces and edges of the mesh that need to be modified,
12063   //     i.e. faces and edges built with one or more duplicated nodes.
12064   //     associate these faces or edges to their corresponding domain.
12065   //     only the first domain found is kept when a face or edge is shared
12066
12067   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12068   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12069
12070   //MESSAGE(".. Modification of elements");
12071   SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
12072   for (int idomain = idom0; idomain < nbDomains; idomain++)
12073   {
12074     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12075     for (; itnod != nodeDomains.end(); ++itnod)
12076     {
12077       int oldId = itnod->first;
12078       //MESSAGE("     node " << oldId);
12079       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
12080       for (int i = 0; i < l.ncells; i++)
12081       {
12082         int vtkId = l.cells[i];
12083         int vtkType = grid->GetCellType(vtkId);
12084         int downId = grid->CellIdToDownId(vtkId);
12085         if (downId < 0)
12086           continue; // new cells: not to be modified
12087         DownIdType aCell(downId, vtkType);
12088         int volParents[1000];
12089         int nbvol = 0;
12090         nbvol = grid->GetParentVolumes(volParents, vtkId);
12091         if ( domainType == SMDSAbs_Volume )
12092         {
12093           nbvol = grid->GetParentVolumes(volParents, vtkId);
12094         }
12095         else // domainType == SMDSAbs_Face
12096         {
12097           const int            nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
12098           const int           *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
12099           const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
12100           for (int i=0; i< nbFaces; i++)
12101           {
12102             int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
12103             if (vtkFaceId >= 0)
12104               volParents[nbvol++] = vtkFaceId;
12105           }
12106         }
12107         for (int j = 0; j < nbvol; j++)
12108           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12109             if (!feDom.count(vtkId))
12110             {
12111               feDom[vtkId] = idomain;
12112               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12113               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
12114               //        << " type " << vtkType << " downId " << downId);
12115             }
12116       }
12117     }
12118   }
12119
12120   // --- iterate on shared faces (volumes to modify, face to extrude)
12121   //     get node id's of the face
12122   //     replace old nodes by new nodes in volumes, and update inverse connectivity
12123
12124   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12125   for (int m=0; m<3; m++)
12126   {
12127     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12128     itface = (*amap).begin();
12129     for (; itface != (*amap).end(); ++itface)
12130     {
12131       DownIdType face = itface->first;
12132       std::set<int> oldNodes;
12133       std::set<int>::iterator itn;
12134       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12135       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12136       std::map<int, int> localClonedNodeIds;
12137
12138       std::map<int, int> domvol = itface->second;
12139       std::map<int, int>::iterator itdom = domvol.begin();
12140       for (; itdom != domvol.end(); ++itdom)
12141       {
12142         int idom = itdom->first;
12143         int vtkVolId = itdom->second;
12144         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12145         localClonedNodeIds.clear();
12146         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12147         {
12148           int oldId = *itn;
12149           if (nodeDomains[oldId].count(idom))
12150           {
12151             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12152             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12153           }
12154         }
12155         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12156       }
12157     }
12158   }
12159
12160   // Remove empty groups (issue 0022812)
12161   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12162   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12163   {
12164     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12165       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12166   }
12167
12168   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12169   grid->DeleteLinks();
12170
12171   CHRONOSTOP(50);
12172   counters::stats();
12173   return true;
12174 }
12175
12176 /*!
12177  * \brief Double nodes on some external faces and create flat elements.
12178  * Flat elements are mainly used by some types of mechanic calculations.
12179  *
12180  * Each group of the list must be constituted of faces.
12181  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12182  * @param theElems - list of groups of faces, where a group of faces is a set of
12183  * SMDS_MeshElements sorted by Id.
12184  * @return TRUE if operation has been completed successfully, FALSE otherwise
12185  */
12186 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12187 {
12188   // MESSAGE("-------------------------------------------------");
12189   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12190   // MESSAGE("-------------------------------------------------");
12191
12192   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12193
12194   // --- For each group of faces
12195   //     duplicate the nodes, create a flat element based on the face
12196   //     replace the nodes of the faces by their clones
12197
12198   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12199   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12200   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12201
12202   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12203   {
12204     const TIDSortedElemSet&           domain = theElems[idom];
12205     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12206     for ( ; elemItr != domain.end(); ++elemItr )
12207     {
12208       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12209       if (!aFace)
12210         continue;
12211       // MESSAGE("aFace=" << aFace->GetID());
12212       bool isQuad = aFace->IsQuadratic();
12213       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12214
12215       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12216
12217       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12218       while (nodeIt->more())
12219       {
12220         const SMDS_MeshNode* node = nodeIt->next();
12221         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12222         if (isMedium)
12223           ln2.push_back(node);
12224         else
12225           ln0.push_back(node);
12226
12227         const SMDS_MeshNode* clone = 0;
12228         if (!clonedNodes.count(node))
12229         {
12230           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12231           copyPosition( node, clone );
12232           clonedNodes[node] = clone;
12233         }
12234         else
12235           clone = clonedNodes[node];
12236
12237         if (isMedium)
12238           ln3.push_back(clone);
12239         else
12240           ln1.push_back(clone);
12241
12242         const SMDS_MeshNode* inter = 0;
12243         if (isQuad && (!isMedium))
12244         {
12245           if (!intermediateNodes.count(node))
12246           {
12247             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12248             copyPosition( node, inter );
12249             intermediateNodes[node] = inter;
12250           }
12251           else
12252             inter = intermediateNodes[node];
12253           ln4.push_back(inter);
12254         }
12255       }
12256
12257       // --- extrude the face
12258
12259       vector<const SMDS_MeshNode*> ln;
12260       SMDS_MeshVolume* vol = 0;
12261       vtkIdType aType = aFace->GetVtkType();
12262       switch (aType)
12263       {
12264       case VTK_TRIANGLE:
12265         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12266         // MESSAGE("vol prism " << vol->GetID());
12267         ln.push_back(ln1[0]);
12268         ln.push_back(ln1[1]);
12269         ln.push_back(ln1[2]);
12270         break;
12271       case VTK_QUAD:
12272         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12273         // MESSAGE("vol hexa " << vol->GetID());
12274         ln.push_back(ln1[0]);
12275         ln.push_back(ln1[1]);
12276         ln.push_back(ln1[2]);
12277         ln.push_back(ln1[3]);
12278         break;
12279       case VTK_QUADRATIC_TRIANGLE:
12280         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12281                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12282         // MESSAGE("vol quad prism " << vol->GetID());
12283         ln.push_back(ln1[0]);
12284         ln.push_back(ln1[1]);
12285         ln.push_back(ln1[2]);
12286         ln.push_back(ln3[0]);
12287         ln.push_back(ln3[1]);
12288         ln.push_back(ln3[2]);
12289         break;
12290       case VTK_QUADRATIC_QUAD:
12291         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12292         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12293         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12294         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12295                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12296                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12297         // MESSAGE("vol quad hexa " << vol->GetID());
12298         ln.push_back(ln1[0]);
12299         ln.push_back(ln1[1]);
12300         ln.push_back(ln1[2]);
12301         ln.push_back(ln1[3]);
12302         ln.push_back(ln3[0]);
12303         ln.push_back(ln3[1]);
12304         ln.push_back(ln3[2]);
12305         ln.push_back(ln3[3]);
12306         break;
12307       case VTK_POLYGON:
12308         break;
12309       default:
12310         break;
12311       }
12312
12313       if (vol)
12314       {
12315         stringstream grpname;
12316         grpname << "jf_";
12317         grpname << idom;
12318         string namegrp = grpname.str();
12319         if (!mapOfJunctionGroups.count(namegrp))
12320           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12321         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12322         if (sgrp)
12323           sgrp->Add(vol->GetID());
12324       }
12325
12326       // --- modify the face
12327
12328       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12329     }
12330   }
12331   return true;
12332 }
12333
12334 /*!
12335  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12336  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12337  *  groups of faces to remove inside the object, (idem edges).
12338  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12339  */
12340 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12341                                       const TopoDS_Shape&             theShape,
12342                                       SMESH_NodeSearcher*             theNodeSearcher,
12343                                       const char*                     groupName,
12344                                       std::vector<double>&            nodesCoords,
12345                                       std::vector<std::vector<int> >& listOfListOfNodes)
12346 {
12347   // MESSAGE("--------------------------------");
12348   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12349   // MESSAGE("--------------------------------");
12350
12351   // --- zone of volumes to remove is given :
12352   //     1 either by a geom shape (one or more vertices) and a radius,
12353   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12354   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12355   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12356   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12357   //     defined by it's name.
12358
12359   SMESHDS_GroupBase* groupDS = 0;
12360   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12361   while ( groupIt->more() )
12362   {
12363     groupDS = 0;
12364     SMESH_Group * group = groupIt->next();
12365     if ( !group ) continue;
12366     groupDS = group->GetGroupDS();
12367     if ( !groupDS || groupDS->IsEmpty() ) continue;
12368     std::string grpName = group->GetName();
12369     //MESSAGE("grpName=" << grpName);
12370     if (grpName == groupName)
12371       break;
12372     else
12373       groupDS = 0;
12374   }
12375
12376   bool isNodeGroup = false;
12377   bool isNodeCoords = false;
12378   if (groupDS)
12379   {
12380     if (groupDS->GetType() != SMDSAbs_Node)
12381       return;
12382     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12383   }
12384
12385   if (nodesCoords.size() > 0)
12386     isNodeCoords = true; // a list o nodes given by their coordinates
12387   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12388
12389   // --- define groups to build
12390
12391   // --- group of SMDS volumes
12392   string grpvName = groupName;
12393   grpvName += "_vol";
12394   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12395   if (!grp)
12396   {
12397     MESSAGE("group not created " << grpvName);
12398     return;
12399   }
12400   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12401
12402   // --- group of SMDS faces on the skin
12403   string grpsName = groupName;
12404   grpsName += "_skin";
12405   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12406   if (!grps)
12407   {
12408     MESSAGE("group not created " << grpsName);
12409     return;
12410   }
12411   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12412
12413   // --- group of SMDS faces internal (several shapes)
12414   string grpiName = groupName;
12415   grpiName += "_internalFaces";
12416   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12417   if (!grpi)
12418   {
12419     MESSAGE("group not created " << grpiName);
12420     return;
12421   }
12422   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12423
12424   // --- group of SMDS faces internal (several shapes)
12425   string grpeiName = groupName;
12426   grpeiName += "_internalEdges";
12427   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12428   if (!grpei)
12429   {
12430     MESSAGE("group not created " << grpeiName);
12431     return;
12432   }
12433   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12434
12435   // --- build downward connectivity
12436
12437   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12438   meshDS->BuildDownWardConnectivity(true);
12439   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12440
12441   // --- set of volumes detected inside
12442
12443   std::set<int> setOfInsideVol;
12444   std::set<int> setOfVolToCheck;
12445
12446   std::vector<gp_Pnt> gpnts;
12447
12448   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12449   {
12450     //MESSAGE("group of nodes provided");
12451     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12452     while ( elemIt->more() )
12453     {
12454       const SMDS_MeshElement* elem = elemIt->next();
12455       if (!elem)
12456         continue;
12457       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12458       if (!node)
12459         continue;
12460       SMDS_MeshElement* vol = 0;
12461       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12462       while (volItr->more())
12463       {
12464         vol = (SMDS_MeshElement*)volItr->next();
12465         setOfInsideVol.insert(vol->GetVtkID());
12466         sgrp->Add(vol->GetID());
12467       }
12468     }
12469   }
12470   else if (isNodeCoords)
12471   {
12472     //MESSAGE("list of nodes coordinates provided");
12473     size_t i = 0;
12474     int k = 0;
12475     while ( i < nodesCoords.size()-2 )
12476     {
12477       double x = nodesCoords[i++];
12478       double y = nodesCoords[i++];
12479       double z = nodesCoords[i++];
12480       gp_Pnt p = gp_Pnt(x, y ,z);
12481       gpnts.push_back(p);
12482       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12483       k++;
12484     }
12485   }
12486   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12487   {
12488     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12489     TopTools_IndexedMapOfShape vertexMap;
12490     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12491     gp_Pnt p = gp_Pnt(0,0,0);
12492     if (vertexMap.Extent() < 1)
12493       return;
12494
12495     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12496     {
12497       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12498       p = BRep_Tool::Pnt(vertex);
12499       gpnts.push_back(p);
12500       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12501     }
12502   }
12503
12504   if (gpnts.size() > 0)
12505   {
12506     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12507     //MESSAGE("startNode->nodeId " << nodeId);
12508
12509     double radius2 = radius*radius;
12510     //MESSAGE("radius2 " << radius2);
12511
12512     // --- volumes on start node
12513
12514     setOfVolToCheck.clear();
12515     SMDS_MeshElement* startVol = 0;
12516     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12517     while (volItr->more())
12518     {
12519       startVol = (SMDS_MeshElement*)volItr->next();
12520       setOfVolToCheck.insert(startVol->GetVtkID());
12521     }
12522     if (setOfVolToCheck.empty())
12523     {
12524       MESSAGE("No volumes found");
12525       return;
12526     }
12527
12528     // --- starting with central volumes then their neighbors, check if they are inside
12529     //     or outside the domain, until no more new neighbor volume is inside.
12530     //     Fill the group of inside volumes
12531
12532     std::map<int, double> mapOfNodeDistance2;
12533     std::set<int> setOfOutsideVol;
12534     while (!setOfVolToCheck.empty())
12535     {
12536       std::set<int>::iterator it = setOfVolToCheck.begin();
12537       int vtkId = *it;
12538       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12539       bool volInside = false;
12540       vtkIdType npts = 0;
12541       vtkIdType const *pts(nullptr);
12542       grid->GetCellPoints(vtkId, npts, pts);
12543       for (int i=0; i<npts; i++)
12544       {
12545         double distance2 = 0;
12546         if (mapOfNodeDistance2.count(pts[i]))
12547         {
12548           distance2 = mapOfNodeDistance2[pts[i]];
12549           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12550         }
12551         else
12552         {
12553           double *coords = grid->GetPoint(pts[i]);
12554           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12555           distance2 = 1.E40;
12556           for ( size_t j = 0; j < gpnts.size(); j++ )
12557           {
12558             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12559             if (d2 < distance2)
12560             {
12561               distance2 = d2;
12562               if (distance2 < radius2)
12563                 break;
12564             }
12565           }
12566           mapOfNodeDistance2[pts[i]] = distance2;
12567           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12568         }
12569         if (distance2 < radius2)
12570         {
12571           volInside = true; // one or more nodes inside the domain
12572           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12573           break;
12574         }
12575       }
12576       if (volInside)
12577       {
12578         setOfInsideVol.insert(vtkId);
12579         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12580         int neighborsVtkIds[NBMAXNEIGHBORS];
12581         int downIds[NBMAXNEIGHBORS];
12582         unsigned char downTypes[NBMAXNEIGHBORS];
12583         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12584         for (int n = 0; n < nbNeighbors; n++)
12585           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12586             setOfVolToCheck.insert(neighborsVtkIds[n]);
12587       }
12588       else
12589       {
12590         setOfOutsideVol.insert(vtkId);
12591         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12592       }
12593       setOfVolToCheck.erase(vtkId);
12594     }
12595   }
12596
12597   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12598   //     If yes, add the volume to the inside set
12599
12600   bool addedInside = true;
12601   std::set<int> setOfVolToReCheck;
12602   while (addedInside)
12603   {
12604     //MESSAGE(" --------------------------- re check");
12605     addedInside = false;
12606     std::set<int>::iterator itv = setOfInsideVol.begin();
12607     for (; itv != setOfInsideVol.end(); ++itv)
12608     {
12609       int vtkId = *itv;
12610       int neighborsVtkIds[NBMAXNEIGHBORS];
12611       int downIds[NBMAXNEIGHBORS];
12612       unsigned char downTypes[NBMAXNEIGHBORS];
12613       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12614       for (int n = 0; n < nbNeighbors; n++)
12615         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12616           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12617     }
12618     setOfVolToCheck = setOfVolToReCheck;
12619     setOfVolToReCheck.clear();
12620     while  (!setOfVolToCheck.empty())
12621     {
12622       std::set<int>::iterator it = setOfVolToCheck.begin();
12623       int vtkId = *it;
12624       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12625       {
12626         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12627         int countInside = 0;
12628         int neighborsVtkIds[NBMAXNEIGHBORS];
12629         int downIds[NBMAXNEIGHBORS];
12630         unsigned char downTypes[NBMAXNEIGHBORS];
12631         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12632         for (int n = 0; n < nbNeighbors; n++)
12633           if (setOfInsideVol.count(neighborsVtkIds[n]))
12634             countInside++;
12635         //MESSAGE("countInside " << countInside);
12636         if (countInside > 1)
12637         {
12638           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12639           setOfInsideVol.insert(vtkId);
12640           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12641           addedInside = true;
12642         }
12643         else
12644           setOfVolToReCheck.insert(vtkId);
12645       }
12646       setOfVolToCheck.erase(vtkId);
12647     }
12648   }
12649
12650   // --- map of Downward faces at the boundary, inside the global volume
12651   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12652   //     fill group of SMDS faces inside the volume (when several volume shapes)
12653   //     fill group of SMDS faces on the skin of the global volume (if skin)
12654
12655   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12656   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12657   std::set<int>::iterator it = setOfInsideVol.begin();
12658   for (; it != setOfInsideVol.end(); ++it)
12659   {
12660     int vtkId = *it;
12661     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12662     int neighborsVtkIds[NBMAXNEIGHBORS];
12663     int downIds[NBMAXNEIGHBORS];
12664     unsigned char downTypes[NBMAXNEIGHBORS];
12665     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12666     for (int n = 0; n < nbNeighbors; n++)
12667     {
12668       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12669       if (neighborDim == 3)
12670       {
12671         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12672         {
12673           DownIdType face(downIds[n], downTypes[n]);
12674           boundaryFaces[face] = vtkId;
12675         }
12676         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12677         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12678         if (vtkFaceId >= 0)
12679         {
12680           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12681           // find also the smds edges on this face
12682           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12683           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12684           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12685           for (int i = 0; i < nbEdges; i++)
12686           {
12687             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12688             if (vtkEdgeId >= 0)
12689               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12690           }
12691         }
12692       }
12693       else if (neighborDim == 2) // skin of the volume
12694       {
12695         DownIdType face(downIds[n], downTypes[n]);
12696         skinFaces[face] = vtkId;
12697         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12698         if (vtkFaceId >= 0)
12699           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12700       }
12701     }
12702   }
12703
12704   // --- identify the edges constituting the wire of each subshape on the skin
12705   //     define polylines with the nodes of edges, equivalent to wires
12706   //     project polylines on subshapes, and partition, to get geom faces
12707
12708   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12709   std::set<int>                 shapeIds;
12710
12711   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12712   while (itelem->more())
12713   {
12714     const SMDS_MeshElement *elem = itelem->next();
12715     int shapeId = elem->getshapeId();
12716     int   vtkId = elem->GetVtkID();
12717     if (!shapeIdToVtkIdSet.count(shapeId))
12718     {
12719       shapeIds.insert(shapeId);
12720     }
12721     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12722   }
12723
12724   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12725   std::set<DownIdType, DownIdCompare> emptyEdges;
12726
12727   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12728   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12729   {
12730     int shapeId = itShape->first;
12731     //MESSAGE(" --- Shape ID --- "<< shapeId);
12732     shapeIdToEdges[shapeId] = emptyEdges;
12733
12734     std::vector<int> nodesEdges;
12735
12736     std::set<int>::iterator its = itShape->second.begin();
12737     for (; its != itShape->second.end(); ++its)
12738     {
12739       int vtkId = *its;
12740       //MESSAGE("     " << vtkId);
12741       int neighborsVtkIds[NBMAXNEIGHBORS];
12742       int downIds[NBMAXNEIGHBORS];
12743       unsigned char downTypes[NBMAXNEIGHBORS];
12744       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12745       for (int n = 0; n < nbNeighbors; n++)
12746       {
12747         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12748           continue;
12749         smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12750         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12751         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12752         {
12753           DownIdType edge(downIds[n], downTypes[n]);
12754           if (!shapeIdToEdges[shapeId].count(edge))
12755           {
12756             shapeIdToEdges[shapeId].insert(edge);
12757             int vtkNodeId[3];
12758             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12759             nodesEdges.push_back(vtkNodeId[0]);
12760             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12761             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12762           }
12763         }
12764       }
12765     }
12766
12767     std::list<int> order;
12768     if (nodesEdges.size() > 0)
12769     {
12770       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12771       nodesEdges[0] = -1;
12772       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12773       nodesEdges[1] = -1; // do not reuse this edge
12774       bool found = true;
12775       while (found)
12776       {
12777         int nodeTofind = order.back(); // try first to push back
12778         int i = 0;
12779         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12780           if (nodesEdges[i] == nodeTofind)
12781             break;
12782         if ( i == (int) nodesEdges.size() )
12783           found = false; // no follower found on back
12784         else
12785         {
12786           if (i%2) // odd ==> use the previous one
12787             if (nodesEdges[i-1] < 0)
12788               found = false;
12789             else
12790             {
12791               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12792               nodesEdges[i-1] = -1;
12793             }
12794           else // even ==> use the next one
12795             if (nodesEdges[i+1] < 0)
12796               found = false;
12797             else
12798             {
12799               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12800               nodesEdges[i+1] = -1;
12801             }
12802         }
12803         if (found)
12804           continue;
12805         // try to push front
12806         found = true;
12807         nodeTofind = order.front(); // try to push front
12808         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12809           if ( nodesEdges[i] == nodeTofind )
12810             break;
12811         if ( i == (int)nodesEdges.size() )
12812         {
12813           found = false; // no predecessor found on front
12814           continue;
12815         }
12816         if (i%2) // odd ==> use the previous one
12817           if (nodesEdges[i-1] < 0)
12818             found = false;
12819           else
12820           {
12821             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12822             nodesEdges[i-1] = -1;
12823           }
12824         else // even ==> use the next one
12825           if (nodesEdges[i+1] < 0)
12826             found = false;
12827           else
12828           {
12829             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12830             nodesEdges[i+1] = -1;
12831           }
12832       }
12833     }
12834
12835
12836     std::vector<int> nodes;
12837     nodes.push_back(shapeId);
12838     std::list<int>::iterator itl = order.begin();
12839     for (; itl != order.end(); itl++)
12840     {
12841       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12842       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12843     }
12844     listOfListOfNodes.push_back(nodes);
12845   }
12846
12847   //     partition geom faces with blocFissure
12848   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12849   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12850
12851   return;
12852 }
12853
12854
12855 //================================================================================
12856 /*!
12857  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12858  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12859  * \return TRUE if operation has been completed successfully, FALSE otherwise
12860  */
12861 //================================================================================
12862
12863 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12864 {
12865   // iterates on volume elements and detect all free faces on them
12866   SMESHDS_Mesh* aMesh = GetMeshDS();
12867   if (!aMesh)
12868     return false;
12869
12870   ElemFeatures faceType( SMDSAbs_Face );
12871   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12872   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12873   while(vIt->more())
12874   {
12875     const SMDS_MeshVolume* volume = vIt->next();
12876     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12877     vTool.SetExternalNormal();
12878     const int iQuad = volume->IsQuadratic();
12879     faceType.SetQuad( iQuad );
12880     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12881     {
12882       if (!vTool.IsFreeFace(iface))
12883         continue;
12884       nbFree++;
12885       vector<const SMDS_MeshNode *> nodes;
12886       int nbFaceNodes = vTool.NbFaceNodes(iface);
12887       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12888       int inode = 0;
12889       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12890         nodes.push_back(faceNodes[inode]);
12891
12892       if (iQuad) // add medium nodes
12893       {
12894         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12895           nodes.push_back(faceNodes[inode]);
12896         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12897           nodes.push_back(faceNodes[8]);
12898       }
12899       // add new face based on volume nodes
12900       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12901       {
12902         nbExisted++; // face already exists
12903       }
12904       else
12905       {
12906         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12907         nbCreated++;
12908       }
12909     }
12910   }
12911   return ( nbFree == ( nbExisted + nbCreated ));
12912 }
12913
12914 namespace
12915 {
12916   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12917   {
12918     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12919       return n;
12920     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12921   }
12922 }
12923 //================================================================================
12924 /*!
12925  * \brief Creates missing boundary elements
12926  *  \param elements - elements whose boundary is to be checked
12927  *  \param dimension - defines type of boundary elements to create
12928  *  \param group - a group to store created boundary elements in
12929  *  \param targetMesh - a mesh to store created boundary elements in
12930  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12931  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12932  *                                boundary elements will be copied into the targetMesh
12933  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12934  *                                boundary elements will be added into the new group
12935  *  \param aroundElements - if true, elements will be created on boundary of given
12936  *                          elements else, on boundary of the whole mesh.
12937  * \return nb of added boundary elements
12938  */
12939 //================================================================================
12940
12941 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12942                                        Bnd_Dimension           dimension,
12943                                        SMESH_Group*            group/*=0*/,
12944                                        SMESH_Mesh*             targetMesh/*=0*/,
12945                                        bool                    toCopyElements/*=false*/,
12946                                        bool                    toCopyExistingBoundary/*=false*/,
12947                                        bool                    toAddExistingBondary/*= false*/,
12948                                        bool                    aroundElements/*= false*/,
12949                                        bool                    toCreateAllElements/*= false*/)
12950 {
12951   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12952   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12953   // hope that all elements are of the same type, do not check them all
12954   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12955     throw SALOME_Exception(LOCALIZED("wrong element type"));
12956
12957   if ( !targetMesh )
12958     toCopyElements = toCopyExistingBoundary = false;
12959
12960   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12961   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12962   int nbAddedBnd = 0;
12963
12964   // editor adding present bnd elements and optionally holding elements to add to the group
12965   SMESH_MeshEditor* presentEditor;
12966   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12967   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12968   SMESH_MesherHelper helper( *myMesh );
12969   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12970   SMDS_VolumeTool vTool;
12971   TIDSortedElemSet avoidSet;
12972   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12973   size_t inode;
12974
12975   typedef vector<const SMDS_MeshNode*> TConnectivity;
12976   TConnectivity tgtNodes;
12977   ElemFeatures elemKind( missType ), elemToCopy;
12978
12979   vector<const SMDS_MeshElement*> presentBndElems;
12980   vector<TConnectivity>           missingBndElems;
12981   vector<int>                     freeFacets;
12982   TConnectivity nodes, elemNodes;
12983
12984   SMDS_ElemIteratorPtr eIt;
12985   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12986   else                  eIt = SMESHUtils::elemSetIterator( elements );
12987
12988   while ( eIt->more() )
12989   {
12990     const SMDS_MeshElement* elem = eIt->next();
12991     const int              iQuad = elem->IsQuadratic();
12992     elemKind.SetQuad( iQuad );
12993
12994     // ------------------------------------------------------------------------------------
12995     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12996     // ------------------------------------------------------------------------------------
12997     presentBndElems.clear();
12998     missingBndElems.clear();
12999     freeFacets.clear(); nodes.clear(); elemNodes.clear();
13000     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13001     {
13002       const SMDS_MeshElement* otherVol = 0;
13003       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13004       {
13005         if ( !toCreateAllElements && 
13006               !vTool.IsFreeFace(iface, &otherVol) &&
13007                 ( !aroundElements || elements.count( otherVol )))
13008           continue;
13009         freeFacets.push_back( iface );
13010       }
13011       if ( missType == SMDSAbs_Face )
13012         vTool.SetExternalNormal();
13013       for ( size_t i = 0; i < freeFacets.size(); ++i )
13014       {
13015         int                iface = freeFacets[i];
13016         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13017         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13018         if ( missType == SMDSAbs_Edge ) // boundary edges
13019         {
13020           nodes.resize( 2+iQuad );
13021           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13022           {
13023             for ( size_t j = 0; j < nodes.size(); ++j )
13024               nodes[ j ] = nn[ i+j ];
13025             if ( const SMDS_MeshElement* edge =
13026                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13027               presentBndElems.push_back( edge );
13028             else
13029               missingBndElems.push_back( nodes );
13030           }
13031         }
13032         else // boundary face
13033         {
13034           nodes.clear();
13035           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13036             nodes.push_back( nn[inode] ); // add corner nodes
13037           if (iQuad)
13038             for ( inode = 1; inode < nbFaceNodes; inode += 2)
13039               nodes.push_back( nn[inode] ); // add medium nodes
13040           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13041           if ( iCenter > 0 )
13042             nodes.push_back( vTool.GetNodes()[ iCenter ] );
13043
13044           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13045                                                                SMDSAbs_Face, /*noMedium=*/false ))
13046             presentBndElems.push_back( f );
13047           else
13048             missingBndElems.push_back( nodes );
13049
13050           if ( targetMesh != myMesh )
13051           {
13052             // add 1D elements on face boundary to be added to a new mesh
13053             const SMDS_MeshElement* edge;
13054             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13055             {
13056               if ( iQuad )
13057                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13058               else
13059                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13060               if ( edge && avoidSet.insert( edge ).second )
13061                 presentBndElems.push_back( edge );
13062             }
13063           }
13064         }
13065       }
13066     }
13067     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13068     {
13069       avoidSet.clear(), avoidSet.insert( elem );
13070       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
13071                         SMDS_MeshElement::iterator() );
13072       elemNodes.push_back( elemNodes[0] );
13073       nodes.resize( 2 + iQuad );
13074       const int nbLinks = elem->NbCornerNodes();
13075       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13076       {
13077         nodes[0] = elemNodes[iN];
13078         nodes[1] = elemNodes[iN+1+iQuad];
13079         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13080           continue; // not free link
13081
13082         if ( iQuad ) nodes[2] = elemNodes[iN+1];
13083         if ( const SMDS_MeshElement* edge =
13084              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13085           presentBndElems.push_back( edge );
13086         else
13087           missingBndElems.push_back( nodes );
13088       }
13089     }
13090
13091     // ---------------------------------
13092     // 2. Add missing boundary elements
13093     // ---------------------------------
13094     if ( targetMesh != myMesh )
13095       // instead of making a map of nodes in this mesh and targetMesh,
13096       // we create nodes with same IDs.
13097       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13098       {
13099         TConnectivity& srcNodes = missingBndElems[i];
13100         tgtNodes.resize( srcNodes.size() );
13101         for ( inode = 0; inode < srcNodes.size(); ++inode )
13102           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13103         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13104                                                                        missType,
13105                                                                        /*noMedium=*/false))
13106           continue;
13107         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13108         ++nbAddedBnd;
13109       }
13110     else
13111       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13112       {
13113         TConnectivity& nodes = missingBndElems[ i ];
13114         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
13115                                                                        missType,
13116                                                                        /*noMedium=*/false))
13117           continue;
13118         SMDS_MeshElement* newElem =
13119           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13120         nbAddedBnd += bool( newElem );
13121
13122         // try to set a new element to a shape
13123         if ( myMesh->HasShapeToMesh() )
13124         {
13125           bool ok = true;
13126           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13127           const size_t nbN = nodes.size() / (iQuad+1 );
13128           for ( inode = 0; inode < nbN && ok; ++inode )
13129           {
13130             pair<int, TopAbs_ShapeEnum> i_stype =
13131               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13132             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13133               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13134           }
13135           if ( ok && mediumShapes.size() > 1 )
13136           {
13137             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13138             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13139             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13140             {
13141               if (( ok = ( stype_i->first != stype_i_0.first )))
13142                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13143                                         aMesh->IndexToShape( stype_i_0.second ));
13144             }
13145           }
13146           if ( ok && mediumShapes.begin()->first == missShapeType )
13147             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13148         }
13149       }
13150
13151     // ----------------------------------
13152     // 3. Copy present boundary elements
13153     // ----------------------------------
13154     if ( toCopyExistingBoundary )
13155       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13156       {
13157         const SMDS_MeshElement* e = presentBndElems[i];
13158         tgtNodes.resize( e->NbNodes() );
13159         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13160           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13161         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13162       }
13163     else // store present elements to add them to a group
13164       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13165       {
13166         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13167       }
13168
13169   } // loop on given elements
13170
13171   // ---------------------------------------------
13172   // 4. Fill group with boundary elements
13173   // ---------------------------------------------
13174   if ( group )
13175   {
13176     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13177       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13178         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13179   }
13180   tgtEditor.myLastCreatedElems.clear();
13181   tgtEditor2.myLastCreatedElems.clear();
13182
13183   // -----------------------
13184   // 5. Copy given elements
13185   // -----------------------
13186   if ( toCopyElements && targetMesh != myMesh )
13187   {
13188     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13189     else                  eIt = SMESHUtils::elemSetIterator( elements );
13190     while (eIt->more())
13191     {
13192       const SMDS_MeshElement* elem = eIt->next();
13193       tgtNodes.resize( elem->NbNodes() );
13194       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13195         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13196       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13197
13198       tgtEditor.myLastCreatedElems.clear();
13199     }
13200   }
13201   return nbAddedBnd;
13202 }
13203
13204 //================================================================================
13205 /*!
13206  * \brief Copy node position and set \a to node on the same geometry
13207  */
13208 //================================================================================
13209
13210 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13211                                      const SMDS_MeshNode* to )
13212 {
13213   if ( !from || !to ) return;
13214
13215   SMDS_PositionPtr pos = from->GetPosition();
13216   if ( !pos || from->getshapeId() < 1 ) return;
13217
13218   switch ( pos->GetTypeOfPosition() )
13219   {
13220   case SMDS_TOP_3DSPACE: break;
13221
13222   case SMDS_TOP_FACE:
13223   {
13224     SMDS_FacePositionPtr fPos = pos;
13225     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13226                                 fPos->GetUParameter(), fPos->GetVParameter() );
13227     break;
13228   }
13229   case SMDS_TOP_EDGE:
13230   {
13231     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13232     SMDS_EdgePositionPtr ePos = pos;
13233     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13234     break;
13235   }
13236   case SMDS_TOP_VERTEX:
13237   {
13238     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13239     break;
13240   }
13241   case SMDS_TOP_UNSPEC:
13242   default:;
13243   }
13244 }